aboutsummaryrefslogtreecommitdiffstats
path: root/executor/common_linux.h
diff options
context:
space:
mode:
authorTamas Koczka <poprdi@google.com>2022-11-02 10:30:06 +0000
committerDmitry Vyukov <dvyukov@google.com>2022-11-07 09:57:47 -0800
commit6feb842be06bf94e4751c499cd8b4659974c6f03 (patch)
tree1432308b34d4581248d39133a6758d341a47fc41 /executor/common_linux.h
parenta779b11a80536d17c6097a8169f009d6597a078e (diff)
executor: fix "wrong response packet" in BT fuzzing (#3493)
Problem: the BT initialization logic (`initialize_vhci()` in `common_linux.h`) expected `HCI_VENDOR_PKT` to be sent first, but this is not always the case as the kernel sends these two packets almost at the same time (both are sent as the result of the `open("/dev/vhci", …)` call): * syscall thread: `HCI_VENDOR_PKT` (in `__vhci_create_device`) * `power_on` queue thread: `HCI_OP_RESET` (from `hci_reset_sync` <- `hci_init1_sync` <- `hci_init_sync` <- `hci_dev_open_sync` <- `hci_dev_do_open` <- `hci_power_on` <- `hdev->power_on` <- (worker queue) <- `hci_register_dev` <- `__vhci_create_device`) Solution: handle both `HCI_OP_RESET` and `HCI_VENDOR_PKT` packets in `initialize_vhci`. Also instead of waiting for the kernel to send `HCI_VENDOR_PKT` after 1 second, we initiate the setup by sending `HCI_VENDOR_PKT` (request) to the kernel first.
Diffstat (limited to 'executor/common_linux.h')
-rw-r--r--executor/common_linux.h48
1 files changed, 37 insertions, 11 deletions
diff --git a/executor/common_linux.h b/executor/common_linux.h
index 39a687e36..10f693815 100644
--- a/executor/common_linux.h
+++ b/executor/common_linux.h
@@ -2586,11 +2586,21 @@ struct hci_dev_req {
uint32 dev_opt;
};
-struct vhci_vendor_pkt {
+struct vhci_vendor_pkt_request {
uint8 type;
uint8 opcode;
- uint16 id;
-};
+} __attribute__((packed));
+
+struct vhci_pkt {
+ uint8 type;
+ union {
+ struct {
+ uint8 opcode;
+ uint16 id;
+ } __attribute__((packed)) vendor_pkt;
+ struct hci_command_hdr command_hdr;
+ };
+} __attribute__((packed));
#define HCIDEVUP _IOW('H', 201, int)
#define HCISETSCAN _IOW('H', 221, int)
@@ -2718,6 +2728,9 @@ static void* event_thread(void* arg)
#define HCI_HANDLE_1 200
#define HCI_HANDLE_2 201
+#define HCI_PRIMARY 0
+#define HCI_OP_RESET 0x0c03
+
static void initialize_vhci()
{
#if SYZ_EXECUTOR
@@ -2741,25 +2754,38 @@ static void initialize_vhci()
close(vhci_fd);
vhci_fd = kVhciFd;
- struct vhci_vendor_pkt vendor_pkt;
- if (read(vhci_fd, &vendor_pkt, sizeof(vendor_pkt)) != sizeof(vendor_pkt))
- fail("read failed");
+ struct vhci_vendor_pkt_request vendor_pkt_req = {HCI_VENDOR_PKT, HCI_PRIMARY};
+ if (write(vhci_fd, &vendor_pkt_req, sizeof(vendor_pkt_req)) != sizeof(vendor_pkt_req))
+ fail("vendor_pkt_req write failed");
+
+ struct vhci_pkt vhci_pkt;
+ if (read(vhci_fd, &vhci_pkt, sizeof(vhci_pkt)) != sizeof(vhci_pkt))
+ fail("vhci_pkt read failed");
+
+ if (vhci_pkt.type == HCI_COMMAND_PKT && vhci_pkt.command_hdr.opcode == HCI_OP_RESET) {
+ char response[1] = {0};
+ hci_send_event_cmd_complete(vhci_fd, HCI_OP_RESET, response, sizeof(response));
+
+ if (read(vhci_fd, &vhci_pkt, sizeof(vhci_pkt)) != sizeof(vhci_pkt))
+ fail("vhci_pkt read failed");
+ }
- if (vendor_pkt.type != HCI_VENDOR_PKT)
+ if (vhci_pkt.type != HCI_VENDOR_PKT)
fail("wrong response packet");
- debug("hci dev id: %x\n", vendor_pkt.id);
+ int dev_id = vhci_pkt.vendor_pkt.id;
+ debug("hci dev id: %x\n", dev_id);
pthread_t th;
if (pthread_create(&th, NULL, event_thread, NULL))
fail("pthread_create failed");
// Bring hci device up
- int ret = ioctl(hci_sock, HCIDEVUP, vendor_pkt.id);
+ int ret = ioctl(hci_sock, HCIDEVUP, dev_id);
if (ret) {
if (errno == ERFKILL) {
rfkill_unblock_all();
- ret = ioctl(hci_sock, HCIDEVUP, vendor_pkt.id);
+ ret = ioctl(hci_sock, HCIDEVUP, dev_id);
}
if (ret && errno != EALREADY)
@@ -2768,7 +2794,7 @@ static void initialize_vhci()
// Activate page scanning mode which is required to fake a connection.
struct hci_dev_req dr = {0};
- dr.dev_id = vendor_pkt.id;
+ dr.dev_id = dev_id;
dr.dev_opt = SCAN_PAGE;
if (ioctl(hci_sock, HCISETSCAN, &dr))
fail("ioctl(HCISETSCAN) failed");