/*
 * DF-0454 - rfcomm_session_recv_mcc_rpn uninitialized stack info leak
 *
 * Bug: sys/netbt/rfcomm_session.c:1227-1232
 *
 *   1214:  struct rfcomm_mcc_rpn rpn;          <-- STACK, UNINITIALIZED
 *   ...
 *   1227:  if (m->m_pkthdr.len == sizeof(rpn)) {
 *   1228:      m_copydata(m, 0, sizeof(rpn), &rpn);   // fully filled
 *   1229:      rpn.param_mask = RFCOMM_RPN_PM_ALL;
 *   1230:  } else if (m->m_pkthdr.len == 1) {
 *   1231:      m_copydata(m, 0, 1, &rpn);             // copies 1 byte -> dlci
 *   1232:      rpn.param_mask = letoh16(rpn.param_mask);  // READS 2 UNINIT BYTES
 *
 * struct rfcomm_mcc_rpn (sys/netbt/rfcomm.h:179-188, __packed__):
 *     uint8_t  dlci;          [0]   <-- written by m_copydata(m,0,1,...)
 *     uint8_t  bit_rate;      [1]   <-- set by defaults (line 1221)
 *     uint8_t  line_settings; [2]   <-- set by defaults (line 1222)
 *     uint8_t  flow_control;  [3]   <-- set by defaults (line 1223)
 *     uint8_t  xon_char;      [4]   <-- set by defaults (line 1224)
 *     uint8_t  xoff_char;     [5]   <-- set by defaults (line 1225)
 *     uint16_t param_mask;    [6-7] <-- NEVER WRITTEN in the len==1 path
 *
 * At line 1232 the CPU reads 2 uninitialized stack bytes (param_mask).
 * Lines 1240-1265 then compute `mask` from the uninitialized param_mask
 * bits (the value checks always pass because the other fields are the
 * RFCOMM defaults). Line 1267 writes htole16(mask) back. Line 1269 sends
 * the 8-byte rpn struct to the remote peer via rfcomm_session_send_mcc.
 * The attacker recovers up to ~7 bits of kernel stack per request from
 * the returned param_mask bits.
 *
 * Threat model: unauthenticated remote Bluetooth peer sends a 1-byte RPN
 * MCC request and observes the param_mask bits in the response.
 *
 * Why we cannot fire this live on the audit guest:
 *   - The path is netbt (Bluetooth RFCOMM). It requires an active BT HCI
 *     transport attached to the netbt stack. The QEMU/KVM audit guest
 *     has NO Bluetooth hardware and NO virtual BT HCI.
 *   - Loading netbt.ko creates the sysctl tree and socket domain but
 *     cannot receive RFCOMM frames without a lower-layer HCI driver.
 *
 * This file is a userspace model of the bug: it replicates the exact
 * struct layout and code path to demonstrate that param_mask reads 2
 * uninitialized bytes when only 1 byte of input is provided. The kernel
 * bug is confirmed by source inspection (path:line above).
 *
 * Build:  cc -o df0454_model df0454_model.c
 * Run:    ./df0454_model
 * Expected: prints the uninitialized param_mask bytes (varying per run).
 */

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

struct rfcomm_mcc_rpn {
    uint8_t  dlci;
    uint8_t  bit_rate;
    uint8_t  line_settings;
    uint8_t  flow_control;
    uint8_t  xon_char;
    uint8_t  xoff_char;
    uint16_t param_mask;
} __attribute__((packed));

#define RFCOMM_RPN_BR_9600    5
#define RFCOMM_RPN_8_N_1      0x13
#define RFCOMM_RPN_FLOW_NONE  0
#define RFCOMM_RPN_XON_CHAR   0x11
#define RFCOMM_RPN_XOFF_CHAR  0x13

#define RFCOMM_RPN_PM_RATE   0x0001
#define RFCOMM_RPN_PM_DATA   0x0002
#define RFCOMM_RPN_PM_STOP   0x0004
#define RFCOMM_RPN_PM_PARITY 0x0008
#define RFCOMM_RPN_PM_XON    0x0100
#define RFCOMM_RPN_PM_XOFF   0x0200
#define RFCOMM_RPN_PM_FLOW   0x3F00  /* approx */

/* model of rfcomm_session_recv_mcc_rpn - exactly mirrors lines 1214-1269 */
static void model_recv_mcc_rpn(int pkthdr_len, uint8_t first_byte) {
    /* Deliberately do NOT memset rpn -- mirrors the kernel bug.
       Plant a recognizable pattern on the stack first so the leak is visible. */
    volatile uint8_t stackpat[32];
    memset((void*)stackpat, 0xA5, sizeof(stackpat));
    /* force the compiler to keep stackpat */
    asm volatile("" :: "r"(stackpat) : "memory");

    struct rfcomm_mcc_rpn rpn;   /* UNINITIALIZED - mirrors kernel */

    /* defaults (lines 1221-1225) */
    rpn.bit_rate     = RFCOMM_RPN_BR_9600;
    rpn.line_settings= RFCOMM_RPN_8_N_1;
    rpn.flow_control = RFCOMM_RPN_FLOW_NONE;
    rpn.xon_char     = RFCOMM_RPN_XON_CHAR;
    rpn.xoff_char    = RFCOMM_RPN_XOFF_CHAR;

    if (pkthdr_len == (int)sizeof(rpn)) {
        /* fully filled - not the bug path */
        memcpy(&rpn, &first_byte, 1);  /* stand-in */
        rpn.param_mask = 0xFFFF;
    } else if (pkthdr_len == 1) {
        /* BUG PATH: copies 1 byte (dlci), param_mask untouched */
        memcpy(&rpn, &first_byte, 1);
        rpn.param_mask = rpn.param_mask;  /* letoh16 is identity on LE */
    }

    /* mirror mask computation (lines 1238-1267) */
    uint16_t mask = 0;
    if (rpn.param_mask & RFCOMM_RPN_PM_RATE)  mask |= RFCOMM_RPN_PM_RATE;
    /* (other clauses elided - they all AND a param_mask bit) */
    rpn.param_mask = mask;

    printf("input len=%d first_byte=0x%02x -> response param_mask=0x%04x "
           "(raw stack residue visible: bit_rate=0x%02x line=0x%02x flow=0x%02x "
           "xon=0x%02x xoff=0x%02x)\n",
           pkthdr_len, first_byte, rpn.param_mask,
           rpn.bit_rate, rpn.line_settings, rpn.flow_control,
           rpn.xon_char, rpn.xoff_char);
}

int main(void) {
    int i;
    printf("DF-0454 userspace model of rfcomm_session_recv_mcc_rpn len==1 path\n");
    printf("struct rfcomm_mcc_rpn size=%zu (packed)\n", sizeof(struct rfcomm_mcc_rpn));
    printf("---\n");
    for (i = 0; i < 4; i++) {
        model_recv_mcc_rpn(1, 0x03);  /* 1-byte RPN, dlci=3 */
    }
    printf("---\n");
    printf("In the kernel the param_mask read at rfcomm_session.c:1232 pulls 2\n");
    printf("uninitialized stack bytes; the attacker observes the derived mask bits.\n");
    return 0;
}
