diff options
| author | TheOfficialFloW <theflow@google.com> | 2020-07-30 11:33:48 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-07-30 11:33:48 +0200 |
| commit | b0947553167615d7bb1b67b22d2d080e5a5ab2cd (patch) | |
| tree | f3f104edb509ef1cf89c1db3119052f4d7d4e7ae /executor | |
| parent | 233283a191b3c32a48c56928985c8e2cfc004aeb (diff) | |
all: initialize vhci in linux
* all: initialize vhci in linux
* executor/common_linux.h: improve vhci initialization
* pkg/repro/repro.go: add missing vhci options
* executor/common_linux.h: fix type and add missing header
* executor, pkg: do it like NetInjection
* pkg/csource/csource.go: do not emit syz_emit_vhci if vhci is not enabled
* executor/common_linux.h: fix format string
* executor/common_linux.h: initialize with memset
For som reason {0} gets complains about missing braces...
* executor/common_linux.h: simplify vhci init
* executor/common_linux.h: try to bring all available hci devices up
* executor/common_linux.h: find which hci device has been registered
* executor/common_linux.h: use HCI_VENDOR_PKT response to retrieve device id
* sys/linux/dev_vhci.txt: fix structs of inquiry and report packets
* executor/common_linux.h: remove unnecessary return statement and check vendor_pkt read size
* executor/common_linux.h: remove unnecessary return statement and check vendor_pkt read size
* sys/linux/dev_vhci.txt: pack extended_inquiry_info_t
* sys/linux/l2cap.txt: add l2cap_conf_opt struct
* executor/common_linux.h: just fill bd addr will 0xaa
* executor/common_linux.h: just fill bd addr will 0xaa
Diffstat (limited to 'executor')
| -rw-r--r-- | executor/common_linux.h | 307 | ||||
| -rw-r--r-- | executor/executor.cc | 2 |
2 files changed, 306 insertions, 3 deletions
diff --git a/executor/common_linux.h b/executor/common_linux.h index a7dfa2374..b679d1ebb 100644 --- a/executor/common_linux.h +++ b/executor/common_linux.h @@ -557,7 +557,7 @@ static void initialize_tun(void) } #endif -#if SYZ_EXECUTOR || __NR_syz_init_net_socket || SYZ_DEVLINK_PCI +#if SYZ_EXECUTOR || __NR_syz_init_net_socket || SYZ_DEVLINK_PCI || SYZ_VHCI_INJECTION const int kInitNetNsFd = 239; // see kMaxFd #endif @@ -1768,7 +1768,7 @@ static long syz_open_pts(volatile long a0, volatile long a1) } #endif -#if SYZ_EXECUTOR || __NR_syz_init_net_socket +#if SYZ_EXECUTOR || __NR_syz_init_net_socket || SYZ_VHCI_INJECTION #if SYZ_EXECUTOR || SYZ_SANDBOX_NONE || SYZ_SANDBOX_SETUID || SYZ_SANDBOX_NAMESPACE || SYZ_SANDBOX_ANDROID #include <fcntl.h> #include <sched.h> @@ -1801,6 +1801,291 @@ static long syz_init_net_socket(volatile long domain, volatile long type, volati #endif #endif +#if SYZ_EXECUTOR || SYZ_VHCI_INJECTION +#include <errno.h> +#include <fcntl.h> +#include <pthread.h> +#include <sys/epoll.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/uio.h> + +#define BTPROTO_HCI 1 +#define ACL_LINK 1 +#define SCAN_PAGE 2 + +typedef struct { + uint8 b[6]; +} __attribute__((packed)) bdaddr_t; + +#define HCI_COMMAND_PKT 1 +#define HCI_EVENT_PKT 4 +#define HCI_VENDOR_PKT 0xff + +struct hci_command_hdr { + uint16 opcode; + uint8 plen; +} __attribute__((packed)); + +struct hci_event_hdr { + uint8 evt; + uint8 plen; +} __attribute__((packed)); + +#define HCI_EV_CONN_COMPLETE 0x03 +struct hci_ev_conn_complete { + uint8 status; + uint16 handle; + bdaddr_t bdaddr; + uint8 link_type; + uint8 encr_mode; +} __attribute__((packed)); + +#define HCI_EV_CONN_REQUEST 0x04 +struct hci_ev_conn_request { + bdaddr_t bdaddr; + uint8 dev_class[3]; + uint8 link_type; +} __attribute__((packed)); + +#define HCI_EV_REMOTE_FEATURES 0x0b +struct hci_ev_remote_features { + uint8 status; + uint16 handle; + uint8 features[8]; +} __attribute__((packed)); + +#define HCI_EV_CMD_COMPLETE 0x0e +struct hci_ev_cmd_complete { + uint8 ncmd; + uint16 opcode; +} __attribute__((packed)); + +#define HCI_OP_WRITE_SCAN_ENABLE 0x0c1a + +#define HCI_OP_READ_BUFFER_SIZE 0x1005 +struct hci_rp_read_buffer_size { + uint8 status; + uint16 acl_mtu; + uint8 sco_mtu; + uint16 acl_max_pkt; + uint16 sco_max_pkt; +} __attribute__((packed)); + +#define HCI_OP_READ_BD_ADDR 0x1009 +struct hci_rp_read_bd_addr { + uint8 status; + bdaddr_t bdaddr; +} __attribute__((packed)); + +struct hci_dev_req { + uint16 dev_id; + uint32 dev_opt; +}; + +struct vhci_vendor_pkt { + uint8 type; + uint8 opcode; + uint16 id; +}; + +#define HCIDEVUP _IOW('H', 201, int) +#define HCISETSCAN _IOW('H', 221, int) + +static int vhci_fd = -1; + +static void hci_send_event_packet(int fd, uint8 evt, void* data, size_t data_len) +{ + struct iovec iv[3]; + + struct hci_event_hdr hdr; + hdr.evt = evt; + hdr.plen = data_len; + + uint8 type = HCI_EVENT_PKT; + + iv[0].iov_base = &type; + iv[0].iov_len = sizeof(type); + iv[1].iov_base = &hdr; + iv[1].iov_len = sizeof(hdr); + iv[2].iov_base = data; + iv[2].iov_len = data_len; + + if (writev(fd, iv, sizeof(iv) / sizeof(struct iovec)) < 0) + fail("writev failed"); +} + +static void hci_send_event_cmd_complete(int fd, uint16 opcode, void* data, size_t data_len) +{ + struct iovec iv[4]; + + struct hci_event_hdr hdr; + hdr.evt = HCI_EV_CMD_COMPLETE; + hdr.plen = sizeof(struct hci_ev_cmd_complete) + data_len; + + struct hci_ev_cmd_complete evt_hdr; + evt_hdr.ncmd = 1; + evt_hdr.opcode = opcode; + + uint8 type = HCI_EVENT_PKT; + + iv[0].iov_base = &type; + iv[0].iov_len = sizeof(type); + iv[1].iov_base = &hdr; + iv[1].iov_len = sizeof(hdr); + iv[2].iov_base = &evt_hdr; + iv[2].iov_len = sizeof(evt_hdr); + iv[3].iov_base = data; + iv[3].iov_len = data_len; + + if (writev(fd, iv, sizeof(iv) / sizeof(struct iovec)) < 0) + fail("writev failed"); +} + +static bool process_command_pkt(int fd, char* buf, ssize_t buf_size) +{ + struct hci_command_hdr* hdr = (struct hci_command_hdr*)buf; + if (buf_size < (ssize_t)sizeof(struct hci_command_hdr) || + hdr->plen != buf_size - sizeof(struct hci_command_hdr)) { + fail("invalid size: %zx\n", buf_size); + } + + switch (hdr->opcode) { + case HCI_OP_WRITE_SCAN_ENABLE: { + uint8 status = 0; + hci_send_event_cmd_complete(fd, hdr->opcode, &status, sizeof(status)); + return true; + } + case HCI_OP_READ_BD_ADDR: { + struct hci_rp_read_bd_addr rp = {0}; + rp.status = 0; + memset(&rp.bdaddr, 0xaa, 6); + hci_send_event_cmd_complete(fd, hdr->opcode, &rp, sizeof(rp)); + return false; + } + case HCI_OP_READ_BUFFER_SIZE: { + struct hci_rp_read_buffer_size rp = {0}; + rp.status = 0; + rp.acl_mtu = 1021; + rp.sco_mtu = 96; + rp.acl_max_pkt = 4; + rp.sco_max_pkt = 6; + hci_send_event_cmd_complete(fd, hdr->opcode, &rp, sizeof(rp)); + return false; + } + } + + char dummy[0xf9] = {0}; + hci_send_event_cmd_complete(fd, hdr->opcode, dummy, sizeof(dummy)); + return false; +} + +static void* event_thread(void* arg) +{ + while (1) { + char buf[1024] = {0}; + ssize_t buf_size = read(vhci_fd, buf, sizeof(buf)); + if (buf_size < 0) + fail("read failed"); + debug_dump_data(buf, buf_size); + if (buf_size > 0 && buf[0] == HCI_COMMAND_PKT) { + if (process_command_pkt(vhci_fd, buf + 1, buf_size - 1)) + break; + } + } + return NULL; +} + +#define ACL_HANDLE 256 + +static void initialize_vhci() +{ +#if SYZ_EXECUTOR + if (!flag_vhci_injection) + return; +#endif + + int hci_sock = syz_init_net_socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); + if (hci_sock < 0) + fail("syz_init_net_socket failed"); + + vhci_fd = open("/dev/vhci", O_RDWR); + if (vhci_fd == -1) + fail("open /dev/vhci failed"); + + // Remap vhci onto higher fd number to hide it from fuzzer and to keep + // fd numbers stable regardless of whether vhci is opened or not (also see kMaxFd). + const int kVhciFd = 241; + if (dup2(vhci_fd, kVhciFd) < 0) + fail("dup2(vhci_fd, kVhciFd) failed"); + 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"); + + if (vendor_pkt.type != HCI_VENDOR_PKT) + fail("wrong response packet"); + + debug("hci dev id: %x\n", vendor_pkt.id); + + pthread_t th; + if (pthread_create(&th, NULL, event_thread, NULL)) + fail("pthread_create failed"); + + // Bring hci device up + if (ioctl(hci_sock, HCIDEVUP, vendor_pkt.id) && errno != EALREADY) + fail("ioctl(HCIDEVUP) failed"); + + // 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_opt = SCAN_PAGE; + if (ioctl(hci_sock, HCISETSCAN, &dr)) + fail("ioctl(HCISETSCAN) failed"); + + // Fake a connection with bd address aa:aa:aa:aa:aa:aa. + // This is a fixed address used in sys/linux/socket_bluetooth.txt. + struct hci_ev_conn_request request; + memset(&request, 0, sizeof(request)); + memset(&request.bdaddr, 0xaa, 6); + request.link_type = ACL_LINK; + hci_send_event_packet(vhci_fd, HCI_EV_CONN_REQUEST, &request, sizeof(request)); + + struct hci_ev_conn_complete complete; + memset(&complete, 0, sizeof(complete)); + complete.status = 0; + complete.handle = ACL_HANDLE; + memset(&complete.bdaddr, 0xaa, 6); + complete.link_type = ACL_LINK; + complete.encr_mode = 0; + hci_send_event_packet(vhci_fd, HCI_EV_CONN_COMPLETE, &complete, sizeof(complete)); + + struct hci_ev_remote_features features; + memset(&features, 0, sizeof(features)); + features.status = 0; + features.handle = ACL_HANDLE; + hci_send_event_packet(vhci_fd, HCI_EV_REMOTE_FEATURES, &features, sizeof(features)); + + pthread_join(th, NULL); + close(hci_sock); +} +#endif + +#if SYZ_EXECUTOR || __NR_syz_emit_vhci && SYZ_VHCI_INJECTION +static long syz_emit_vhci(volatile long a0, volatile long a1) +{ + if (vhci_fd < 0) + return (uintptr_t)-1; + + char* data = (char*)a0; + uint32 length = a1; + + return write(vhci_fd, data, length); +} +#endif + #if SYZ_EXECUTOR || __NR_syz_genetlink_get_family_id #include <errno.h> #include <linux/genetlink.h> @@ -2778,7 +3063,7 @@ static void sandbox_common() setpgrp(); setsid(); -#if SYZ_EXECUTOR || __NR_syz_init_net_socket || SYZ_DEVLINK_PCI +#if SYZ_EXECUTOR || __NR_syz_init_net_socket || SYZ_DEVLINK_PCI || SYZ_VHCI_INJECTION int netns = open("/proc/self/ns/net", O_RDONLY); if (netns == -1) fail("open(/proc/self/ns/net) failed"); @@ -2927,6 +3212,9 @@ static int do_sandbox_none(void) #if SYZ_EXECUTOR || SYZ_NET_DEVICES initialize_netdevices(); #endif +#if SYZ_EXECUTOR || SYZ_VHCI_INJECTION + initialize_vhci(); +#endif loop(); doexit(1); } @@ -2964,6 +3252,9 @@ static int do_sandbox_setuid(void) #if SYZ_EXECUTOR || SYZ_NET_DEVICES initialize_netdevices(); #endif +#if SYZ_EXECUTOR || SYZ_VHCI_INJECTION + initialize_vhci(); +#endif const int nobody = 65534; if (setgroups(0, NULL)) @@ -3024,6 +3315,11 @@ static int namespace_sandbox_proc(void* arg) #if SYZ_EXECUTOR || SYZ_NET_DEVICES initialize_netdevices(); #endif +#if SYZ_EXECUTOR || SYZ_VHCI_INJECTION + // This will fail and we only have it here to avoid complains about the + // function being unused. + initialize_vhci(); +#endif if (mkdir("./syz-tmp", 0777)) fail("mkdir(syz-tmp) failed"); @@ -3240,6 +3536,11 @@ static int do_sandbox_android(void) // and try to reinitialize them as other test processes use them. initialize_netdevices(); #endif +#if SYZ_EXECUTOR || SYZ_VHCI_INJECTION + // This will fail and we only have it here to avoid complains about the + // function being unused. + initialize_vhci(); +#endif if (chown(".", UNTRUSTED_APP_UID, UNTRUSTED_APP_UID) != 0) fail("chmod failed"); diff --git a/executor/executor.cc b/executor/executor.cc index ccbdb1aa0..5b7371b1d 100644 --- a/executor/executor.cc +++ b/executor/executor.cc @@ -128,6 +128,7 @@ static bool flag_net_reset; static bool flag_cgroups; static bool flag_close_fds; static bool flag_devlink_pci; +static bool flag_vhci_injection; static bool flag_collect_cover; static bool flag_dedup_cover; @@ -490,6 +491,7 @@ void parse_env_flags(uint64 flags) flag_cgroups = flags & (1 << 9); flag_close_fds = flags & (1 << 10); flag_devlink_pci = flags & (1 << 11); + flag_vhci_injection = flags & (1 << 12); } #if SYZ_EXECUTOR_USES_FORK_SERVER |
