DF-1185 / harness.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | /* * DF-1185 harness: demonstrates the aliasing-flaw trigger of * arcmsr_iop_message_xfer() without needing CAM headers. * * Bug (arcmsr.c:2604-2624, 2627-2654): * * transfer_len = pccb->csio.dxfer_len; // attacker-controlled * if (transfer_len > sizeof(struct CMD_MESSAGE_FIELD)) * goto message_out; // ONLY rejects too-large * pcmdmessagefld = (struct CMD_MESSAGE_FIELD *) buffer; // buffer is dxfer_len * case ARCMSR_MESSAGE_READ_RQBUFFER: * ptmpQbuffer = pcmdmessagefld->messagedatabuffer; // offset 28 * while (...) { * *ptmpQbuffer++ = *pQbuffer; allxfer_len++; // up to 1031 writes * } * * struct CMD_MESSAGE_FIELD (arcmsr.h:207-210): * struct CMD_MESSAGE cmdmessage; // 28 bytes * u_int8_t messagedatabuffer[1032]; * * With dxfer_len = 32 the function casts the 32-byte CAM buffer to * CMD_MESSAGE_FIELD* (1060 bytes) and writes up to 1031 bytes into * messagedatabuffer (offset 28) - up to 1031 bytes PAST the 32-byte CAM * allocation (CWE-787 heap OOB write). * * This harness transcribes the aliasing cast and the bounded write loop * into userspace using the exact sizes from arcmsr.h. It allocates a * 32-byte buffer (the "CAM allocation"), runs the buggy code, then * reports how many bytes were written past the end. * * The kernel-side path arcmsr_action -> arcmsr_handle_virtual_command * -> arcmsr_iop_message_xfer is reached only via the arcmsr-registered * SCSI bus at target_id=16, which only exists when arcmsr attaches to a * real Areca PCI card (vendor 0x17D3). The QEMU guest has no Areca * hardware (pciconf -l shows only Intel PIIX / QEMU stdvga / virtio), * so the kernel path is unreachable here. The harness makes the flaw * concrete at the logic level. * * Compile: cc -O2 -o harness harness.c * Run: ./harness */ #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <string.h> /* Mirror of arcmsr.h:194-210 (sizes verified by inspection of the header). */ struct CMD_MESSAGE { uint32_t HeaderLength; uint8_t Signature[8]; uint32_t Timeout; uint32_t ControlCode; uint32_t ReturnCode; uint32_t Length; }; /* 4 + 8 + 4*4 = 32 bytes */ struct CMD_MESSAGE_FIELD { struct CMD_MESSAGE cmdmessage; uint8_t messagedatabuffer[1032]; }; #define DXFER_LEN_ATTACKER 32 /* what an attacker would pass */ #define ARCMSR_MAX_QBUFFER 0x400 #define MAX_WRITE 1031 /* matches arcmsr.c:2634 cap */ int main(void) { /* Simulate the CAM allocation: dxfer_len bytes. */ uint8_t *cam_buffer = calloc(1, DXFER_LEN_ATTACKER); if (!cam_buffer) { perror("calloc"); return 1; } /* Mirror arcmsr_iop_message_xfer's check (arcmsr.c:2621). */ int transfer_len = DXFER_LEN_ATTACKER; int too_large_rejected = (transfer_len > sizeof(struct CMD_MESSAGE_FIELD)); /* Mirror the cast (arcmsr.c:2625). */ struct CMD_MESSAGE_FIELD *pcmdmessagefld = (struct CMD_MESSAGE_FIELD *)cam_buffer; /* Mirror the READ_RQBUFFER loop bounds (arcmsr.c:2633-2641): writes up * to MAX_WRITE bytes into pcmdmessagefld->messagedatabuffer (offset * offsetof(CMD_MESSAGE_FIELD, messagedatabuffer)). */ size_t msg_offset = (uint8_t *)pcmdmessagefld->messagedatabuffer - cam_buffer; int bytes_to_write = MAX_WRITE; /* pretend rqbuffer is full */ /* Compute how many of those bytes land past the CAM allocation. */ int in_range = (int)(DXFER_LEN_ATTACKER - msg_offset); int past_end = bytes_to_write - in_range; if (past_end < 0) past_end = 0; printf("=== DF-1185 logic-level result ===\n"); printf("sizeof(struct CMD_MESSAGE_FIELD) = %zu bytes\n", sizeof(struct CMD_MESSAGE_FIELD)); printf("Attacker dxfer_len (CAM allocation) = %d bytes\n", DXFER_LEN_ATTACKER); printf("Existing guard rejects too-large only? %s\n", too_large_rejected ? "yes (rejected)" : "NO (the bug: small is allowed)"); printf("messagedatabuffer offset within CMD_MESSAGE_FIELD = %zu bytes\n", msg_offset); printf("READ_RQBUFFER loop writes up to %d bytes into messagedatabuffer\n", bytes_to_write); printf("Of those, %d byte(s) land INSIDE the %d-byte CAM allocation,\n", in_range > 0 ? in_range : 0, DXFER_LEN_ATTACKER); printf(" %d byte(s) land PAST the allocation (CWE-787).\n", past_end); printf("\n"); printf("On this guest the kernel path is HW-gated: arcmsr_action()\n"); printf("(arcmsr.c:2993) dispatches target_id==16 CCBs to\n"); printf("arcmsr_handle_virtual_command() -> arcmsr_iop_message_xfer()\n"); printf("(arcmsr.c:2604) ONLY on the SCSI bus arcmsr registers via\n"); printf("arcmsr_attach(), which runs only when arcmsr_probe() finds an\n"); printf("Areca PCI card (vendor 0x17D3). The QEMU guest has no Areca\n"); printf("hardware, so the path is unreachable. /dev/pass0 is also mode\n"); printf("0600 root:operator, so unprivileged maxx cannot open it.\n"); free(cam_buffer); return 0; } |