/*
 * DF-0458 - ng_l2cap L2CA_Ping heap over-read via unchecked echo_size vs arglen
 *
 * Bug: sys/netgraph7/bluetooth/l2cap/ng_l2cap_ulpi.c:1297-1358
 *
 *   1306:  if (msg->header.arglen < sizeof(*ip)) {   // sizeof(*ip) = 8
 *             error = EMSGSIZE; goto out;
 *         }
 *   1316:  if (ip->echo_size > NG_L2CAP_MAX_ECHO_SIZE) {  // 65531
 *             error = EMSGSIZE; goto out;
 *         }
 *   ...
 *   1357:  _ng_l2cap_echo_req(cmd->aux, cmd->ident,
 *   1358:      msg->data + sizeof(*ip), ip->echo_size);
 *
 * _ng_l2cap_echo_req (sys/netgraph7/bluetooth/l2cap/ng_l2cap_cmds.h:323-344)
 * calls m_copyback(m, sizeof(*c), echo_size, data) which reads `echo_size`
 * bytes from `data` = msg->data + sizeof(*ip). The available bytes at that
 * pointer are arglen - sizeof(*ip). The only length check is arglen>=8
 * (line 1306) and echo_size<=65531 (line 1316). There is NO check that
 * arglen >= sizeof(*ip) + echo_size.
 *
 * So a message with arglen=8 but echo_size=65531 makes m_copyback read
 * 65531 bytes starting at msg->data+8 -- 65523 bytes past the end of the
 * allocated message buffer, into kernel heap. Those bytes are echoed in
 * the L2CAP EchoReq to the remote peer (kernel heap info leak over BT),
 * and if the read crosses an unmapped page the kernel panics (DoS).
 *
 * Threat model: local privileged netgraph control message sender, OR
 * the remote BT peer if an attacker can influence the L2CA_Ping request
 * sent on the host's behalf. The echo bytes are returned over the air.
 *
 * Why we cannot fire this live on the audit guest:
 *   - The L2CA_Ping path needs an L2CAP connection (con = ng_l2cap_con_by_addr).
 *     Without one, the function tries ng_l2cap_lp_con_req (line 1328) which
 *     requires an attached HCI lower layer. The audit guest has no Bluetooth
 *     hardware / virtual HCI, so no connection can be established and the
 *     m_copyback at line 1357 is never reached.
 *   - Confirmed by source: the OOB read is gated behind con != NULL (or a
 *     successful lp_con_req that creates a connection).
 *
 * The bug is confirmed by source inspection. This file documents the exact
 * data flow and the missing check (arglen < sizeof(*ip) + echo_size).
 *
 * No buildable PoC -- the path is unreachable without a BT HCI transport.
 */

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

int main(void) {
    uint32_t arglen = 8;              /* minimum that passes line 1306 */
    uint32_t echo_size = 65531;       /* maximum that passes line 1316 */
    uint32_t avail = arglen - 8;      /* bytes after the ip header */
    uint32_t read_oob = echo_size - avail;

    printf("DF-0458 source-level analysis (no live trigger - no BT HW)\n");
    printf("---\n");
    printf("Attacker message:  arglen=%u, echo_size=%u\n", arglen, echo_size);
    printf("Check at :1306:    arglen(%u) < sizeof(*ip)(8)?  %s -> passes\n",
           arglen, arglen < 8 ? "YES" : "no");
    printf("Check at :1316:    echo_size(%u) > 65531?         %s -> passes\n",
           echo_size, echo_size > 65531 ? "YES" : "no");
    printf("MISSING check:     arglen(%u) < sizeof(*ip)+echo_size(%u)?  YES\n",
           arglen, 8 + echo_size);
    printf("m_copyback reads %u bytes from msg->data+8; only %u available.\n",
           echo_size, avail);
    printf("=> %u bytes read OUT OF BOUNDS into kernel heap.\n", read_oob);
    printf("Those bytes are echoed in the L2CAP EchoReq response.\n");
    printf("Fix: add  if (arglen < sizeof(*ip) + ip->echo_size) { EMSGSIZE; goto out; }\n");
    return 0;
}
