diff --git a/executor/common_linux.h b/executor/common_linux.h index 756bd870..63b769f4 100644 --- a/executor/common_linux.h +++ b/executor/common_linux.h @@ -107,7 +107,8 @@ static bool write_file(const char* file, const char* what, ...) } #endif -#if SYZ_EXECUTOR || SYZ_NET_DEVICES || SYZ_NET_INJECTION || SYZ_DEVLINK_PCI || __NR_syz_genetlink_get_family_id +#if SYZ_EXECUTOR || SYZ_NET_DEVICES || SYZ_NET_INJECTION || SYZ_DEVLINK_PCI || SYZ_WIFI || \ + __NR_syz_genetlink_get_family_id || __NR_syz_80211_inject_frame || __NR_syz_80211_join_ibss #include #include #include @@ -201,7 +202,8 @@ static int netlink_send_ext(struct nlmsg* nlmsg, int sock, return ((struct nlmsgerr*)(hdr + 1))->error; } -#if SYZ_EXECUTOR || SYZ_NET_DEVICES || SYZ_NET_INJECTION || SYZ_DEVLINK_PCI +#if SYZ_EXECUTOR || SYZ_NET_DEVICES || SYZ_NET_INJECTION || SYZ_DEVLINK_PCI || SYZ_WIFI || \ + __NR_syz_80211_join_ibss || __NR_syz_80211_inject_frame static int netlink_send(struct nlmsg* nlmsg, int sock) { return netlink_send_ext(nlmsg, sock, 0, NULL); @@ -471,7 +473,7 @@ static void netlink_add_neigh(struct nlmsg* nlmsg, int sock, const char* name, #endif #endif -#if SYZ_EXECUTOR || SYZ_NET_DEVICES || SYZ_NET_INJECTION || SYZ_DEVLINK_PCI +#if SYZ_EXECUTOR || SYZ_NET_DEVICES || SYZ_NET_INJECTION || SYZ_DEVLINK_PCI || SYZ_WIFI static struct nlmsg nlmsg; #endif @@ -728,6 +730,278 @@ static void initialize_devlink_pci(void) #endif #endif +#if SYZ_EXECUTOR || SYZ_WIFI || __NR_syz_80211_inject_frame || __NR_syz_80211_join_ibss + +#define WIFI_INITIAL_DEVICE_COUNT 2 +#define WIFI_MAC_BASE \ + { \ + 0x08, 0x02, 0x11, 0x00, 0x00, 0x00 \ + } +#define WIFI_IBSS_BSSID \ + { \ + 0x50, 0x50, 0x50, 0x50, 0x50, 0x50 \ + } +#define WIFI_IBSS_SSID \ + { \ + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 \ + } +#define WIFI_DEFAULT_FREQUENCY 2412 +#define WIFI_DEFAULT_SIGNAL 0 +#define WIFI_DEFAULT_RX_RATE 1 + +// consts from drivers/net/wireless/mac80211_hwsim.h +#define HWSIM_CMD_REGISTER 1 +#define HWSIM_CMD_FRAME 2 +#define HWSIM_CMD_NEW_RADIO 4 +#define HWSIM_ATTR_SUPPORT_P2P_DEVICE 14 +#define HWSIM_ATTR_PERM_ADDR 22 + +#endif + +#if SYZ_EXECUTOR || SYZ_WIFI || __NR_syz_80211_join_ibss +#include +#include +#include +#include +#include +#include +#include + +// From linux/if.h, but we cannot include the file as it conflicts with net/if.h +#define IF_OPER_UP 6 + +// IBSS parameters for nl80211_join_ibss +struct join_ibss_props { + int wiphy_freq; + bool wiphy_freq_fixed; + uint8* mac; + uint8* ssid; + int ssid_len; +}; + +static int set_interface_state(const char* interface_name, int on) +{ + struct ifreq ifr; + int sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + debug("set_interface_state: failed to open socket, errno %d\n", errno); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, interface_name); + int ret = ioctl(sock, SIOCGIFFLAGS, &ifr); + if (ret < 0) { + debug("set_interface_state: failed to execute SIOCGIFFLAGS, ret %d\n", ret); + close(sock); + return -1; + } + + if (on) + ifr.ifr_flags |= IFF_UP; + else + ifr.ifr_flags &= ~IFF_UP; + + ret = ioctl(sock, SIOCSIFFLAGS, &ifr); + close(sock); + if (ret < 0) { + debug("set_interface_state: failed to execute SIOCSIFFLAGS, ret %d\n", ret); + return -1; + } + return 0; +} + +static int nl80211_set_interface(struct nlmsg* nlmsg, int sock, int nl80211_family, uint32 ifindex, uint32 iftype) +{ + struct genlmsghdr genlhdr; + + memset(&genlhdr, 0, sizeof(genlhdr)); + genlhdr.cmd = NL80211_CMD_SET_INTERFACE; + netlink_init(nlmsg, nl80211_family, 0, &genlhdr, sizeof(genlhdr)); + netlink_attr(nlmsg, NL80211_ATTR_IFINDEX, &ifindex, sizeof(ifindex)); + netlink_attr(nlmsg, NL80211_ATTR_IFTYPE, &iftype, sizeof(iftype)); + int err = netlink_send(nlmsg, sock); + if (err < 0) { + debug("nl80211_set_interface failed: %s\n", strerror(-err)); + return -1; + } + return 0; +} + +static int nl80211_join_ibss(struct nlmsg* nlmsg, int sock, int nl80211_family, uint32 ifindex, struct join_ibss_props* props) +{ + struct genlmsghdr genlhdr; + + memset(&genlhdr, 0, sizeof(genlhdr)); + genlhdr.cmd = NL80211_CMD_JOIN_IBSS; + netlink_init(nlmsg, nl80211_family, 0, &genlhdr, sizeof(genlhdr)); + netlink_attr(nlmsg, NL80211_ATTR_IFINDEX, &ifindex, sizeof(ifindex)); + netlink_attr(nlmsg, NL80211_ATTR_SSID, props->ssid, props->ssid_len); + netlink_attr(nlmsg, NL80211_ATTR_WIPHY_FREQ, &(props->wiphy_freq), sizeof(props->wiphy_freq)); + if (props->mac) + netlink_attr(nlmsg, NL80211_ATTR_MAC, props->mac, ETH_ALEN); + if (props->wiphy_freq_fixed) + netlink_attr(nlmsg, NL80211_ATTR_FREQ_FIXED, NULL, 0); + int err = netlink_send(nlmsg, sock); + if (err < 0) { + debug("nl80211_join_ibss failed: %s\n", strerror(-err)); + return -1; + } + return 0; +} + +static int get_ifla_operstate(struct nlmsg* nlmsg, int ifindex) +{ + struct ifinfomsg info; + memset(&info, 0, sizeof(info)); + info.ifi_family = AF_UNSPEC; + info.ifi_index = ifindex; + + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock == -1) { + debug("get_ifla_operstate: socket failed: %d\n", errno); + return -1; + } + + netlink_init(nlmsg, RTM_GETLINK, 0, &info, sizeof(info)); + int n; + int err = netlink_send_ext(nlmsg, sock, RTM_NEWLINK, &n); + close(sock); + + if (err) { + debug("get_ifla_operstate: failed to query: %s\n", strerror(-err)); + return -1; + } + + struct rtattr* attr = IFLA_RTA(NLMSG_DATA(nlmsg->buf)); + for (; RTA_OK(attr, n); attr = RTA_NEXT(attr, n)) { + if (attr->rta_type == IFLA_OPERSTATE) + return *((int32_t*)RTA_DATA(attr)); + } + + return -1; +} + +static int await_ifla_operstate(struct nlmsg* nlmsg, char* interface, int operstate) +{ + int ifindex = if_nametoindex(interface); + while (true) { + usleep(1000); // 1 ms + int ret = get_ifla_operstate(nlmsg, ifindex); + if (ret < 0) + return ret; + if (ret == operstate) + return 0; + } + return 0; +} + +static int nl80211_setup_ibss_interface(struct nlmsg* nlmsg, int sock, int nl80211_family_id, char* interface, struct join_ibss_props* ibss_props) +{ + int ifindex = if_nametoindex(interface); + if (ifindex == 0) { + debug("nl80211_setup_ibss_interface: if_nametoindex failed for %.32s, ret 0\n", interface); + return -1; + } + + int ret = nl80211_set_interface(nlmsg, sock, nl80211_family_id, ifindex, NL80211_IFTYPE_ADHOC); + if (ret < 0) { + debug("nl80211_setup_ibss_interface: nl80211_set_interface failed for %.32s, ret %d\n", interface, ret); + return -1; + } + + ret = set_interface_state(interface, 1); + if (ret < 0) { + debug("nl80211_setup_ibss_interface: set_interface_state failed for %.32s, ret %d\n", interface, ret); + return -1; + } + + ret = nl80211_join_ibss(nlmsg, sock, nl80211_family_id, ifindex, ibss_props); + if (ret < 0) { + debug("nl80211_setup_ibss_interface: nl80211_join_ibss failed for %.32s, ret %d\n", interface, ret); + return -1; + } + + return 0; +} +#endif + +#if SYZ_EXECUTOR || SYZ_WIFI +static int hwsim80211_create_device(struct nlmsg* nlmsg, int sock, int hwsim_family, uint8 mac_addr[ETH_ALEN]) +{ + struct genlmsghdr genlhdr; + memset(&genlhdr, 0, sizeof(genlhdr)); + genlhdr.cmd = HWSIM_CMD_NEW_RADIO; + netlink_init(nlmsg, hwsim_family, 0, &genlhdr, sizeof(genlhdr)); + netlink_attr(nlmsg, HWSIM_ATTR_SUPPORT_P2P_DEVICE, NULL, 0); + netlink_attr(nlmsg, HWSIM_ATTR_PERM_ADDR, mac_addr, ETH_ALEN); + int err = netlink_send(nlmsg, sock); + if (err < 0) { + debug("hwsim80211_create_device failed: %s\n", strerror(-err)); + return -1; + } + return 0; +} + +static void initialize_wifi_devices(void) +{ + // Set up virtual wifi devices and join them into an IBSS network. + // An IBSS network is created here in order to put these devices in an operable state right from + // the beginning. It has the following positive effects. + // 1. Frame injection becomes possible from the very start. + // 2. A number of nl80211 commands expect their target wireless interface to be in an operable state. + // 3. Simplification of reproducer generation - in many cases the reproducer will not have to spend time + // selecting system calls that set up the environment. + // + // IBSS network was chosen as the simplest network type to begin with. + +#if SYZ_EXECUTOR + if (!flag_wifi) + return; +#endif + uint8 mac_addr[6] = WIFI_MAC_BASE; + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + if (sock < 0) { + debug("initialize_wifi_devices: failed to create socket (%d)\n", errno); + return; + } + + int hwsim_family_id = netlink_query_family_id(&nlmsg, sock, "MAC80211_HWSIM"); + int nl80211_family_id = netlink_query_family_id(&nlmsg, sock, "nl80211"); + uint8 ssid[] = WIFI_IBSS_SSID; + uint8 bssid[] = WIFI_IBSS_BSSID; + struct join_ibss_props ibss_props = { + .wiphy_freq = WIFI_DEFAULT_FREQUENCY, .wiphy_freq_fixed = true, .mac = bssid, .ssid = ssid, .ssid_len = sizeof(ssid)}; + + for (int device_id = 0; device_id < WIFI_INITIAL_DEVICE_COUNT; device_id++) { + // Virtual wifi devices will have consequtive mac addresses + mac_addr[5] = device_id; + int ret = hwsim80211_create_device(&nlmsg, sock, hwsim_family_id, mac_addr); + if (ret < 0) + fail("initialize_wifi_devices: failed to create device #%d\n", device_id); + + // For each device, unless HWSIM_ATTR_NO_VIF is passed, a network interface is created + // automatically. Such interfaces are named "wlan0", "wlan1" and so on. + char interface[6] = "wlan0"; + interface[4] += device_id; + + if (nl80211_setup_ibss_interface(&nlmsg, sock, nl80211_family_id, interface, &ibss_props) < 0) + fail("initialize_wifi_devices: failed set up IBSS network for #%d\n", device_id); + } + + // Wait for all devices to join the IBSS network + for (int device_id = 0; device_id < WIFI_INITIAL_DEVICE_COUNT; device_id++) { + char interface[6] = "wlan0"; + interface[4] += device_id; + int ret = await_ifla_operstate(&nlmsg, interface, IF_OPER_UP); + if (ret < 0) + fail("initialize_wifi_devices: get_ifla_operstate failed for #%d, ret %d\n", device_id, ret); + } + + close(sock); +} +#endif + #if SYZ_EXECUTOR || SYZ_NET_DEVICES #include #include @@ -3385,6 +3659,9 @@ static int do_sandbox_none(void) #endif #if SYZ_EXECUTOR || SYZ_NET_DEVICES initialize_netdevices(); +#endif +#if SYZ_EXECUTOR || SYZ_WIFI + initialize_wifi_devices(); #endif loop(); doexit(1); @@ -3426,6 +3703,9 @@ static int do_sandbox_setuid(void) #if SYZ_EXECUTOR || SYZ_NET_DEVICES initialize_netdevices(); #endif +#if SYZ_EXECUTOR || SYZ_WIFI + initialize_wifi_devices(); +#endif const int nobody = 65534; if (setgroups(0, NULL)) @@ -3486,6 +3766,9 @@ static int namespace_sandbox_proc(void* arg) #if SYZ_EXECUTOR || SYZ_NET_DEVICES initialize_netdevices(); #endif +#if SYZ_EXECUTOR || SYZ_WIFI + initialize_wifi_devices(); +#endif if (mkdir("./syz-tmp", 0777)) fail("mkdir(syz-tmp) failed"); @@ -4507,3 +4790,169 @@ static volatile long syz_fuse_handle_req(volatile long a0, // /dev/fuse fd. return fuse_send_response(fd, in_hdr, out_hdr); } #endif + +#if SYZ_EXECUTOR || __NR_syz_80211_inject_frame +#include +#include +#include +#include +#include + +// This pseudo syscall performs 802.11 frame injection. +// +// Its current implementation performs the injection by means of mac80211_hwsim. +// The procedure consists of the following steps: +// 1. Open a netlink socket +// 2. Register as an application responsible for wireless medium simulation by executing +// HWSIM_CMD_REGISTER. This is a preq-requisite for the following step. After HWSIM_CMD_REGISTER +// is executed, mac80211_hwsim stops simulating a perfect medium. +// It is also important to note that this command registers a specific socket, not a netlink port. +// 3. Inject a frame to the required interface by executing HWSIM_CMD_FRAME. +// 4. Close the socket. mac80211_hwsim will detect this and return to perfect medium simulation. +// +// Note that we cannot (should not) open a socket, register it once and then use it for frame injection +// throughout the lifetime of a proc. When some socket is registered, mac80211_hwsim does not broadcast +// frames to all interfaces itself. As we do not perform this activity either, a permanently registered +// socket will disrupt normal network operation. + +#define HWSIM_ATTR_RX_RATE 5 +#define HWSIM_ATTR_SIGNAL 6 +#define HWSIM_ATTR_ADDR_RECEIVER 1 +#define HWSIM_ATTR_FRAME 3 + +#define WIFI_MAX_INJECT_LEN 2048 + +static int hwsim_register_socket(struct nlmsg* nlmsg, int sock, int hwsim_family) +{ + struct genlmsghdr genlhdr; + memset(&genlhdr, 0, sizeof(genlhdr)); + genlhdr.cmd = HWSIM_CMD_REGISTER; + netlink_init(nlmsg, hwsim_family, 0, &genlhdr, sizeof(genlhdr)); + int err = netlink_send(nlmsg, sock); + if (err < 0) { + debug("hwsim_register_device failed: %s\n", strerror(-err)); + return -1; + } + return 0; +} + +static int hwsim_inject_frame(struct nlmsg* nlmsg, int sock, int hwsim_family, uint8* mac_addr, uint8* data, int len) +{ + struct genlmsghdr genlhdr; + uint32 rx_rate = WIFI_DEFAULT_RX_RATE; + uint32 signal = WIFI_DEFAULT_SIGNAL; + + memset(&genlhdr, 0, sizeof(genlhdr)); + genlhdr.cmd = HWSIM_CMD_FRAME; + netlink_init(nlmsg, hwsim_family, 0, &genlhdr, sizeof(genlhdr)); + netlink_attr(nlmsg, HWSIM_ATTR_RX_RATE, &rx_rate, sizeof(rx_rate)); + netlink_attr(nlmsg, HWSIM_ATTR_SIGNAL, &signal, sizeof(signal)); + netlink_attr(nlmsg, HWSIM_ATTR_ADDR_RECEIVER, mac_addr, ETH_ALEN); + netlink_attr(nlmsg, HWSIM_ATTR_FRAME, data, len); + int err = netlink_send(nlmsg, sock); + if (err < 0) { + debug("hwsim_inject_frame failed: %s\n", strerror(-err)); + return -1; + } + return 0; +} + +static long syz_80211_inject_frame(volatile long a0, volatile long a1, volatile long a2) +{ + uint8* mac_addr = (uint8*)a0; + uint8* buf = (uint8*)a1; + int buf_len = (int)a2; + struct nlmsg tmp_msg; + + if (buf_len < 0 || buf_len > WIFI_MAX_INJECT_LEN) { + debug("syz_80211_inject_frame: wrong buffer size %d\n", buf_len); + return -1; + } + + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + if (sock < 0) { + debug("syz_80211_inject_frame: socket creation failed, errno %d\n", errno); + return -1; + } + + int hwsim_family_id = netlink_query_family_id(&tmp_msg, sock, "MAC80211_HWSIM"); + int ret = hwsim_register_socket(&tmp_msg, sock, hwsim_family_id); + if (ret < 0) { + debug("syz_80211_inject_frame: failed to register socket, ret %d\n", ret); + close(sock); + return -1; + } + + ret = hwsim_inject_frame(&tmp_msg, sock, hwsim_family_id, mac_addr, buf, buf_len); + close(sock); + if (ret < 0) { + debug("syz_80211_inject_frame: failed to inject message, ret %d\n", ret); + return -1; + } + + return 0; +} + +#endif + +#if SYZ_EXECUTOR || __NR_syz_80211_join_ibss + +#define WIFI_MAX_SSID_LEN 32 + +#define WIFI_JOIN_IBSS_NO_SCAN 0 +#define WIFI_JOIN_IBSS_BG_SCAN 1 +#define WIFI_JOIN_IBSS_BG_NO_SCAN 2 + +static long syz_80211_join_ibss(volatile long a0, volatile long a1, volatile long a2, volatile long a3) +{ + char* interface = (char*)a0; + uint8* ssid = (uint8*)a1; + int ssid_len = (int)a2; + int mode = (int)a3; // This parameter essentially determines whether it will perform a scan + + struct nlmsg tmp_msg; + uint8 bssid[ETH_ALEN] = WIFI_IBSS_BSSID; + + if (ssid_len < 0 || ssid_len > WIFI_MAX_SSID_LEN) { + debug("syz_80211_join_ibss: invalid ssid len %d\n", ssid_len); + return -1; + } + + if (mode < 0 || mode > WIFI_JOIN_IBSS_BG_NO_SCAN) { + debug("syz_80211_join_ibss: invalid mode %d\n", mode); + return -1; + } + + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + if (sock < 0) { + debug("syz_80211_join_ibss: socket creation failed, errno %d\n", errno); + return -1; + } + + int nl80211_family_id = netlink_query_family_id(&tmp_msg, sock, "nl80211"); + struct join_ibss_props ibss_props = { + .wiphy_freq = WIFI_DEFAULT_FREQUENCY, + .wiphy_freq_fixed = (mode == WIFI_JOIN_IBSS_NO_SCAN || mode == WIFI_JOIN_IBSS_BG_NO_SCAN), + .mac = bssid, + .ssid = ssid, + .ssid_len = ssid_len}; + + int ret = nl80211_setup_ibss_interface(&tmp_msg, sock, nl80211_family_id, interface, &ibss_props); + close(sock); + if (ret < 0) { + debug("syz_80211_join_ibss: failed set up IBSS network for %.32s\n", interface); + return -1; + } + + if (mode == WIFI_JOIN_IBSS_NO_SCAN) { + ret = await_ifla_operstate(&tmp_msg, interface, IF_OPER_UP); + if (ret < 0) { + debug("syz_80211_join_ibss: await_ifla_operstate failed for %.32s, ret %d\n", interface, ret); + return -1; + } + } + + return 0; +} + +#endif diff --git a/executor/executor.cc b/executor/executor.cc index e63f9b22..92d25e31 100644 --- a/executor/executor.cc +++ b/executor/executor.cc @@ -129,6 +129,7 @@ static bool flag_cgroups; static bool flag_close_fds; static bool flag_devlink_pci; static bool flag_vhci_injection; +static bool flag_wifi; static bool flag_collect_cover; static bool flag_dedup_cover; @@ -492,6 +493,7 @@ void parse_env_flags(uint64 flags) flag_close_fds = flags & (1 << 10); flag_devlink_pci = flags & (1 << 11); flag_vhci_injection = flags & (1 << 12); + flag_wifi = flags & (1 << 13); } #if SYZ_EXECUTOR_USES_FORK_SERVER diff --git a/pkg/csource/common.go b/pkg/csource/common.go index f1e8a21b..24219018 100644 --- a/pkg/csource/common.go +++ b/pkg/csource/common.go @@ -125,6 +125,7 @@ func commonDefines(p *prog.Prog, opts Options) map[string]bool { "SYZ_HANDLE_SEGV": opts.HandleSegv, "SYZ_REPRO": opts.Repro, "SYZ_TRACE": opts.Trace, + "SYZ_WIFI": opts.Wifi, "SYZ_EXECUTOR_USES_SHMEM": sysTarget.ExecutorUsesShmem, "SYZ_EXECUTOR_USES_FORK_SERVER": sysTarget.ExecutorUsesForkServer, } diff --git a/pkg/csource/generated.go b/pkg/csource/generated.go index 729772c8..d02e2732 100644 --- a/pkg/csource/generated.go +++ b/pkg/csource/generated.go @@ -2338,7 +2338,8 @@ static bool write_file(const char* file, const char* what, ...) } #endif -#if SYZ_EXECUTOR || SYZ_NET_DEVICES || SYZ_NET_INJECTION || SYZ_DEVLINK_PCI || __NR_syz_genetlink_get_family_id +#if SYZ_EXECUTOR || SYZ_NET_DEVICES || SYZ_NET_INJECTION || SYZ_DEVLINK_PCI || SYZ_WIFI || \ + __NR_syz_genetlink_get_family_id || __NR_syz_80211_inject_frame || __NR_syz_80211_join_ibss #include #include #include @@ -2432,7 +2433,8 @@ static int netlink_send_ext(struct nlmsg* nlmsg, int sock, return ((struct nlmsgerr*)(hdr + 1))->error; } -#if SYZ_EXECUTOR || SYZ_NET_DEVICES || SYZ_NET_INJECTION || SYZ_DEVLINK_PCI +#if SYZ_EXECUTOR || SYZ_NET_DEVICES || SYZ_NET_INJECTION || SYZ_DEVLINK_PCI || SYZ_WIFI || \ + __NR_syz_80211_join_ibss || __NR_syz_80211_inject_frame static int netlink_send(struct nlmsg* nlmsg, int sock) { return netlink_send_ext(nlmsg, sock, 0, NULL); @@ -2702,7 +2704,7 @@ static void netlink_add_neigh(struct nlmsg* nlmsg, int sock, const char* name, #endif #endif -#if SYZ_EXECUTOR || SYZ_NET_DEVICES || SYZ_NET_INJECTION || SYZ_DEVLINK_PCI +#if SYZ_EXECUTOR || SYZ_NET_DEVICES || SYZ_NET_INJECTION || SYZ_DEVLINK_PCI || SYZ_WIFI static struct nlmsg nlmsg; #endif @@ -2944,6 +2946,257 @@ static void initialize_devlink_pci(void) #endif #endif +#if SYZ_EXECUTOR || SYZ_WIFI || __NR_syz_80211_inject_frame || __NR_syz_80211_join_ibss + +#define WIFI_INITIAL_DEVICE_COUNT 2 +#define WIFI_MAC_BASE \ + { \ + 0x08, 0x02, 0x11, 0x00, 0x00, 0x00 \ + } +#define WIFI_IBSS_BSSID \ + { \ + 0x50, 0x50, 0x50, 0x50, 0x50, 0x50 \ + } +#define WIFI_IBSS_SSID \ + { \ + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 \ + } +#define WIFI_DEFAULT_FREQUENCY 2412 +#define WIFI_DEFAULT_SIGNAL 0 +#define WIFI_DEFAULT_RX_RATE 1 +#define HWSIM_CMD_REGISTER 1 +#define HWSIM_CMD_FRAME 2 +#define HWSIM_CMD_NEW_RADIO 4 +#define HWSIM_ATTR_SUPPORT_P2P_DEVICE 14 +#define HWSIM_ATTR_PERM_ADDR 22 + +#endif + +#if SYZ_EXECUTOR || SYZ_WIFI || __NR_syz_80211_join_ibss +#include +#include +#include +#include +#include +#include +#include +#define IF_OPER_UP 6 +struct join_ibss_props { + int wiphy_freq; + bool wiphy_freq_fixed; + uint8* mac; + uint8* ssid; + int ssid_len; +}; + +static int set_interface_state(const char* interface_name, int on) +{ + struct ifreq ifr; + int sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + debug("set_interface_state: failed to open socket, errno %d\n", errno); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, interface_name); + int ret = ioctl(sock, SIOCGIFFLAGS, &ifr); + if (ret < 0) { + debug("set_interface_state: failed to execute SIOCGIFFLAGS, ret %d\n", ret); + close(sock); + return -1; + } + + if (on) + ifr.ifr_flags |= IFF_UP; + else + ifr.ifr_flags &= ~IFF_UP; + + ret = ioctl(sock, SIOCSIFFLAGS, &ifr); + close(sock); + if (ret < 0) { + debug("set_interface_state: failed to execute SIOCSIFFLAGS, ret %d\n", ret); + return -1; + } + return 0; +} + +static int nl80211_set_interface(struct nlmsg* nlmsg, int sock, int nl80211_family, uint32 ifindex, uint32 iftype) +{ + struct genlmsghdr genlhdr; + + memset(&genlhdr, 0, sizeof(genlhdr)); + genlhdr.cmd = NL80211_CMD_SET_INTERFACE; + netlink_init(nlmsg, nl80211_family, 0, &genlhdr, sizeof(genlhdr)); + netlink_attr(nlmsg, NL80211_ATTR_IFINDEX, &ifindex, sizeof(ifindex)); + netlink_attr(nlmsg, NL80211_ATTR_IFTYPE, &iftype, sizeof(iftype)); + int err = netlink_send(nlmsg, sock); + if (err < 0) { + debug("nl80211_set_interface failed: %s\n", strerror(-err)); + return -1; + } + return 0; +} + +static int nl80211_join_ibss(struct nlmsg* nlmsg, int sock, int nl80211_family, uint32 ifindex, struct join_ibss_props* props) +{ + struct genlmsghdr genlhdr; + + memset(&genlhdr, 0, sizeof(genlhdr)); + genlhdr.cmd = NL80211_CMD_JOIN_IBSS; + netlink_init(nlmsg, nl80211_family, 0, &genlhdr, sizeof(genlhdr)); + netlink_attr(nlmsg, NL80211_ATTR_IFINDEX, &ifindex, sizeof(ifindex)); + netlink_attr(nlmsg, NL80211_ATTR_SSID, props->ssid, props->ssid_len); + netlink_attr(nlmsg, NL80211_ATTR_WIPHY_FREQ, &(props->wiphy_freq), sizeof(props->wiphy_freq)); + if (props->mac) + netlink_attr(nlmsg, NL80211_ATTR_MAC, props->mac, ETH_ALEN); + if (props->wiphy_freq_fixed) + netlink_attr(nlmsg, NL80211_ATTR_FREQ_FIXED, NULL, 0); + int err = netlink_send(nlmsg, sock); + if (err < 0) { + debug("nl80211_join_ibss failed: %s\n", strerror(-err)); + return -1; + } + return 0; +} + +static int get_ifla_operstate(struct nlmsg* nlmsg, int ifindex) +{ + struct ifinfomsg info; + memset(&info, 0, sizeof(info)); + info.ifi_family = AF_UNSPEC; + info.ifi_index = ifindex; + + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock == -1) { + debug("get_ifla_operstate: socket failed: %d\n", errno); + return -1; + } + + netlink_init(nlmsg, RTM_GETLINK, 0, &info, sizeof(info)); + int n; + int err = netlink_send_ext(nlmsg, sock, RTM_NEWLINK, &n); + close(sock); + + if (err) { + debug("get_ifla_operstate: failed to query: %s\n", strerror(-err)); + return -1; + } + + struct rtattr* attr = IFLA_RTA(NLMSG_DATA(nlmsg->buf)); + for (; RTA_OK(attr, n); attr = RTA_NEXT(attr, n)) { + if (attr->rta_type == IFLA_OPERSTATE) + return *((int32_t*)RTA_DATA(attr)); + } + + return -1; +} + +static int await_ifla_operstate(struct nlmsg* nlmsg, char* interface, int operstate) +{ + int ifindex = if_nametoindex(interface); + while (true) { + usleep(1000); + int ret = get_ifla_operstate(nlmsg, ifindex); + if (ret < 0) + return ret; + if (ret == operstate) + return 0; + } + return 0; +} + +static int nl80211_setup_ibss_interface(struct nlmsg* nlmsg, int sock, int nl80211_family_id, char* interface, struct join_ibss_props* ibss_props) +{ + int ifindex = if_nametoindex(interface); + if (ifindex == 0) { + debug("nl80211_setup_ibss_interface: if_nametoindex failed for %.32s, ret 0\n", interface); + return -1; + } + + int ret = nl80211_set_interface(nlmsg, sock, nl80211_family_id, ifindex, NL80211_IFTYPE_ADHOC); + if (ret < 0) { + debug("nl80211_setup_ibss_interface: nl80211_set_interface failed for %.32s, ret %d\n", interface, ret); + return -1; + } + + ret = set_interface_state(interface, 1); + if (ret < 0) { + debug("nl80211_setup_ibss_interface: set_interface_state failed for %.32s, ret %d\n", interface, ret); + return -1; + } + + ret = nl80211_join_ibss(nlmsg, sock, nl80211_family_id, ifindex, ibss_props); + if (ret < 0) { + debug("nl80211_setup_ibss_interface: nl80211_join_ibss failed for %.32s, ret %d\n", interface, ret); + return -1; + } + + return 0; +} +#endif + +#if SYZ_EXECUTOR || SYZ_WIFI +static int hwsim80211_create_device(struct nlmsg* nlmsg, int sock, int hwsim_family, uint8 mac_addr[ETH_ALEN]) +{ + struct genlmsghdr genlhdr; + memset(&genlhdr, 0, sizeof(genlhdr)); + genlhdr.cmd = HWSIM_CMD_NEW_RADIO; + netlink_init(nlmsg, hwsim_family, 0, &genlhdr, sizeof(genlhdr)); + netlink_attr(nlmsg, HWSIM_ATTR_SUPPORT_P2P_DEVICE, NULL, 0); + netlink_attr(nlmsg, HWSIM_ATTR_PERM_ADDR, mac_addr, ETH_ALEN); + int err = netlink_send(nlmsg, sock); + if (err < 0) { + debug("hwsim80211_create_device failed: %s\n", strerror(-err)); + return -1; + } + return 0; +} + +static void initialize_wifi_devices(void) +{ + +#if SYZ_EXECUTOR + if (!flag_wifi) + return; +#endif + uint8 mac_addr[6] = WIFI_MAC_BASE; + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + if (sock < 0) { + debug("initialize_wifi_devices: failed to create socket (%d)\n", errno); + return; + } + + int hwsim_family_id = netlink_query_family_id(&nlmsg, sock, "MAC80211_HWSIM"); + int nl80211_family_id = netlink_query_family_id(&nlmsg, sock, "nl80211"); + uint8 ssid[] = WIFI_IBSS_SSID; + uint8 bssid[] = WIFI_IBSS_BSSID; + struct join_ibss_props ibss_props = { + .wiphy_freq = WIFI_DEFAULT_FREQUENCY, .wiphy_freq_fixed = true, .mac = bssid, .ssid = ssid, .ssid_len = sizeof(ssid)}; + + for (int device_id = 0; device_id < WIFI_INITIAL_DEVICE_COUNT; device_id++) { + mac_addr[5] = device_id; + int ret = hwsim80211_create_device(&nlmsg, sock, hwsim_family_id, mac_addr); + if (ret < 0) + fail("initialize_wifi_devices: failed to create device #%d\n", device_id); + char interface[6] = "wlan0"; + interface[4] += device_id; + + if (nl80211_setup_ibss_interface(&nlmsg, sock, nl80211_family_id, interface, &ibss_props) < 0) + fail("initialize_wifi_devices: failed set up IBSS network for #%d\n", device_id); + } + for (int device_id = 0; device_id < WIFI_INITIAL_DEVICE_COUNT; device_id++) { + char interface[6] = "wlan0"; + interface[4] += device_id; + int ret = await_ifla_operstate(&nlmsg, interface, IF_OPER_UP); + if (ret < 0) + fail("initialize_wifi_devices: get_ifla_operstate failed for #%d, ret %d\n", device_id, ret); + } + + close(sock); +} +#endif + #if SYZ_EXECUTOR || SYZ_NET_DEVICES #include #include @@ -7628,6 +7881,9 @@ static int do_sandbox_none(void) #endif #if SYZ_EXECUTOR || SYZ_NET_DEVICES initialize_netdevices(); +#endif +#if SYZ_EXECUTOR || SYZ_WIFI + initialize_wifi_devices(); #endif loop(); doexit(1); @@ -7669,6 +7925,9 @@ static int do_sandbox_setuid(void) #if SYZ_EXECUTOR || SYZ_NET_DEVICES initialize_netdevices(); #endif +#if SYZ_EXECUTOR || SYZ_WIFI + initialize_wifi_devices(); +#endif const int nobody = 65534; if (setgroups(0, NULL)) @@ -7716,6 +7975,9 @@ static int namespace_sandbox_proc(void* arg) #if SYZ_EXECUTOR || SYZ_NET_DEVICES initialize_netdevices(); #endif +#if SYZ_EXECUTOR || SYZ_WIFI + initialize_wifi_devices(); +#endif if (mkdir("./syz-tmp", 0777)) fail("mkdir(syz-tmp) failed"); @@ -9148,6 +9410,155 @@ static volatile long syz_fuse_handle_req(volatile long a0, } #endif +#if SYZ_EXECUTOR || __NR_syz_80211_inject_frame +#include +#include +#include +#include +#include + +#define HWSIM_ATTR_RX_RATE 5 +#define HWSIM_ATTR_SIGNAL 6 +#define HWSIM_ATTR_ADDR_RECEIVER 1 +#define HWSIM_ATTR_FRAME 3 + +#define WIFI_MAX_INJECT_LEN 2048 + +static int hwsim_register_socket(struct nlmsg* nlmsg, int sock, int hwsim_family) +{ + struct genlmsghdr genlhdr; + memset(&genlhdr, 0, sizeof(genlhdr)); + genlhdr.cmd = HWSIM_CMD_REGISTER; + netlink_init(nlmsg, hwsim_family, 0, &genlhdr, sizeof(genlhdr)); + int err = netlink_send(nlmsg, sock); + if (err < 0) { + debug("hwsim_register_device failed: %s\n", strerror(-err)); + return -1; + } + return 0; +} + +static int hwsim_inject_frame(struct nlmsg* nlmsg, int sock, int hwsim_family, uint8* mac_addr, uint8* data, int len) +{ + struct genlmsghdr genlhdr; + uint32 rx_rate = WIFI_DEFAULT_RX_RATE; + uint32 signal = WIFI_DEFAULT_SIGNAL; + + memset(&genlhdr, 0, sizeof(genlhdr)); + genlhdr.cmd = HWSIM_CMD_FRAME; + netlink_init(nlmsg, hwsim_family, 0, &genlhdr, sizeof(genlhdr)); + netlink_attr(nlmsg, HWSIM_ATTR_RX_RATE, &rx_rate, sizeof(rx_rate)); + netlink_attr(nlmsg, HWSIM_ATTR_SIGNAL, &signal, sizeof(signal)); + netlink_attr(nlmsg, HWSIM_ATTR_ADDR_RECEIVER, mac_addr, ETH_ALEN); + netlink_attr(nlmsg, HWSIM_ATTR_FRAME, data, len); + int err = netlink_send(nlmsg, sock); + if (err < 0) { + debug("hwsim_inject_frame failed: %s\n", strerror(-err)); + return -1; + } + return 0; +} + +static long syz_80211_inject_frame(volatile long a0, volatile long a1, volatile long a2) +{ + uint8* mac_addr = (uint8*)a0; + uint8* buf = (uint8*)a1; + int buf_len = (int)a2; + struct nlmsg tmp_msg; + + if (buf_len < 0 || buf_len > WIFI_MAX_INJECT_LEN) { + debug("syz_80211_inject_frame: wrong buffer size %d\n", buf_len); + return -1; + } + + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + if (sock < 0) { + debug("syz_80211_inject_frame: socket creation failed, errno %d\n", errno); + return -1; + } + + int hwsim_family_id = netlink_query_family_id(&tmp_msg, sock, "MAC80211_HWSIM"); + int ret = hwsim_register_socket(&tmp_msg, sock, hwsim_family_id); + if (ret < 0) { + debug("syz_80211_inject_frame: failed to register socket, ret %d\n", ret); + close(sock); + return -1; + } + + ret = hwsim_inject_frame(&tmp_msg, sock, hwsim_family_id, mac_addr, buf, buf_len); + close(sock); + if (ret < 0) { + debug("syz_80211_inject_frame: failed to inject message, ret %d\n", ret); + return -1; + } + + return 0; +} + +#endif + +#if SYZ_EXECUTOR || __NR_syz_80211_join_ibss + +#define WIFI_MAX_SSID_LEN 32 + +#define WIFI_JOIN_IBSS_NO_SCAN 0 +#define WIFI_JOIN_IBSS_BG_SCAN 1 +#define WIFI_JOIN_IBSS_BG_NO_SCAN 2 + +static long syz_80211_join_ibss(volatile long a0, volatile long a1, volatile long a2, volatile long a3) +{ + char* interface = (char*)a0; + uint8* ssid = (uint8*)a1; + int ssid_len = (int)a2; + int mode = (int)a3; + + struct nlmsg tmp_msg; + uint8 bssid[ETH_ALEN] = WIFI_IBSS_BSSID; + + if (ssid_len < 0 || ssid_len > WIFI_MAX_SSID_LEN) { + debug("syz_80211_join_ibss: invalid ssid len %d\n", ssid_len); + return -1; + } + + if (mode < 0 || mode > WIFI_JOIN_IBSS_BG_NO_SCAN) { + debug("syz_80211_join_ibss: invalid mode %d\n", mode); + return -1; + } + + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + if (sock < 0) { + debug("syz_80211_join_ibss: socket creation failed, errno %d\n", errno); + return -1; + } + + int nl80211_family_id = netlink_query_family_id(&tmp_msg, sock, "nl80211"); + struct join_ibss_props ibss_props = { + .wiphy_freq = WIFI_DEFAULT_FREQUENCY, + .wiphy_freq_fixed = (mode == WIFI_JOIN_IBSS_NO_SCAN || mode == WIFI_JOIN_IBSS_BG_NO_SCAN), + .mac = bssid, + .ssid = ssid, + .ssid_len = ssid_len}; + + int ret = nl80211_setup_ibss_interface(&tmp_msg, sock, nl80211_family_id, interface, &ibss_props); + close(sock); + if (ret < 0) { + debug("syz_80211_join_ibss: failed set up IBSS network for %.32s\n", interface); + return -1; + } + + if (mode == WIFI_JOIN_IBSS_NO_SCAN) { + ret = await_ifla_operstate(&tmp_msg, interface, IF_OPER_UP); + if (ret < 0) { + debug("syz_80211_join_ibss: await_ifla_operstate failed for %.32s, ret %d\n", interface, ret); + return -1; + } + } + + return 0; +} + +#endif + #elif GOOS_test #include diff --git a/pkg/csource/options.go b/pkg/csource/options.go index cb1a1911..ba74eb37 100644 --- a/pkg/csource/options.go +++ b/pkg/csource/options.go @@ -41,6 +41,7 @@ type Options struct { DevlinkPCI bool `json:"devlinkpci,omitempty"` USB bool `json:"usb,omitempty"` VhciInjection bool `json:"vhci,omitempty"` + Wifi bool `json:"wifi,omitempty"` UseTmpDir bool `json:"tmpdir,omitempty"` HandleSegv bool `json:"segv,omitempty"` @@ -92,6 +93,9 @@ func (opts Options) Check(OS string) error { if opts.VhciInjection { return errors.New("option VhciInjection without sandbox") } + if opts.Wifi { + return errors.New("option Wifi without sandbox") + } } if opts.Sandbox == sandboxNamespace && !opts.UseTmpDir { // This is borken and never worked. @@ -142,6 +146,9 @@ func (opts Options) checkLinuxOnly(OS string) error { if opts.VhciInjection { return fmt.Errorf("option VHCI is not supported on %v", OS) } + if opts.Wifi { + return fmt.Errorf("option Wifi is not supported on %v", OS) + } if opts.Sandbox == sandboxNamespace || (opts.Sandbox == sandboxSetuid && !(OS == openbsd || OS == freebsd || OS == netbsd)) || opts.Sandbox == sandboxAndroid { @@ -171,6 +178,7 @@ func DefaultOpts(cfg *mgrconfig.Config) Options { CloseFDs: true, DevlinkPCI: true, VhciInjection: true, + Wifi: true, UseTmpDir: true, HandleSegv: true, Repro: true, @@ -185,6 +193,7 @@ func DefaultOpts(cfg *mgrconfig.Config) Options { opts.DevlinkPCI = false opts.USB = false opts.VhciInjection = false + opts.Wifi = false } if cfg.Sandbox == "" || cfg.Sandbox == "setuid" { opts.NetReset = false @@ -266,6 +275,7 @@ func defaultFeatures(value bool) Features { "devlink_pci": {"setup devlink PCI device", value}, "usb": {"setup and use /dev/raw-gadget for USB emulation", value}, "vhci": {"setup and use /dev/vhci for hci packet injection", value}, + "wifi": {"setup and use mac80211_hwsim for wifi emulation", value}, } } diff --git a/pkg/csource/options_test.go b/pkg/csource/options_test.go index 0786d611..1c403ae0 100644 --- a/pkg/csource/options_test.go +++ b/pkg/csource/options_test.go @@ -236,6 +236,7 @@ func TestParseFeaturesFlags(t *testing.T) { "devlink_pci": true, "usb": true, "vhci": true, + "wifi": true, }}, {"none", "none", false, map[string]bool{ "tun": false, @@ -247,6 +248,7 @@ func TestParseFeaturesFlags(t *testing.T) { "devlink_pci": false, "usb": false, "vhci": false, + "wifi": false, }}, {"all", "none", true, map[string]bool{ "tun": true, @@ -258,6 +260,7 @@ func TestParseFeaturesFlags(t *testing.T) { "devlink_pci": true, "usb": true, "vhci": true, + "wifi": true, }}, {"", "none", true, map[string]bool{ "tun": false, @@ -269,6 +272,7 @@ func TestParseFeaturesFlags(t *testing.T) { "devlink_pci": false, "usb": false, "vhci": false, + "wifi": false, }}, {"none", "all", true, map[string]bool{ "tun": false, @@ -280,6 +284,7 @@ func TestParseFeaturesFlags(t *testing.T) { "devlink_pci": false, "usb": false, "vhci": false, + "wifi": false, }}, {"none", "", true, map[string]bool{ "tun": true, @@ -291,6 +296,7 @@ func TestParseFeaturesFlags(t *testing.T) { "devlink_pci": true, "usb": true, "vhci": true, + "wifi": true, }}, {"tun,net_dev", "none", true, map[string]bool{ "tun": true, @@ -302,6 +308,7 @@ func TestParseFeaturesFlags(t *testing.T) { "devlink_pci": false, "usb": false, "vhci": false, + "wifi": false, }}, {"none", "cgroups,net_dev", true, map[string]bool{ "tun": true, @@ -313,6 +320,7 @@ func TestParseFeaturesFlags(t *testing.T) { "devlink_pci": true, "usb": true, "vhci": true, + "wifi": true, }}, {"close_fds", "none", true, map[string]bool{ "tun": false, @@ -324,6 +332,7 @@ func TestParseFeaturesFlags(t *testing.T) { "devlink_pci": false, "usb": false, "vhci": false, + "wifi": false, }}, } for i, test := range tests { diff --git a/pkg/host/features.go b/pkg/host/features.go index 07f6487a..8b78637f 100644 --- a/pkg/host/features.go +++ b/pkg/host/features.go @@ -28,6 +28,7 @@ const ( FeatureDevlinkPCI FeatureUSBEmulation FeatureVhciInjection + FeatureWifiEmulation numFeatures ) @@ -67,6 +68,7 @@ func Check(target *prog.Target) (*Features, error) { FeatureDevlinkPCI: {Name: "devlink PCI setup", Reason: unsupported}, FeatureUSBEmulation: {Name: "USB emulation", Reason: unsupported}, FeatureVhciInjection: {Name: "hci packet injection", Reason: unsupported}, + FeatureWifiEmulation: {Name: "wifi device emulation", Reason: unsupported}, } if noHostChecks(target) { return res, nil diff --git a/pkg/host/features_linux.go b/pkg/host/features_linux.go index 230022c3..5a91fc8e 100644 --- a/pkg/host/features_linux.go +++ b/pkg/host/features_linux.go @@ -29,6 +29,7 @@ func init() { checkFeature[FeatureDevlinkPCI] = checkDevlinkPCI checkFeature[FeatureUSBEmulation] = checkUSBEmulation checkFeature[FeatureVhciInjection] = checkVhciInjection + checkFeature[FeatureWifiEmulation] = checkWifiEmulation } func checkCoverage() string { @@ -219,3 +220,10 @@ func checkDevlinkPCI() string { } return "" } + +func checkWifiEmulation() string { + if err := osutil.IsAccessible("/sys/class/mac80211_hwsim/"); err != nil { + return err.Error() + } + return "" +} diff --git a/pkg/host/syscalls_linux.go b/pkg/host/syscalls_linux.go index 192e8810..c66342dd 100644 --- a/pkg/host/syscalls_linux.go +++ b/pkg/host/syscalls_linux.go @@ -186,6 +186,11 @@ func isVhciInjectionSupported(c *prog.Syscall, target *prog.Target, sandbox stri return reason == "", reason } +func isWifiEmulationSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) { + reason := checkWifiEmulation() + return reason == "", reason +} + func isSyzKvmSetupCPUSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) { switch c.Name { case "syz_kvm_setup_cpu$x86": @@ -284,9 +289,11 @@ var syzkallSupport = map[string]func(*prog.Syscall, *prog.Target, string) (bool, "syz_io_uring_setup": isSyzIoUringSupported, // syz_memcpy_off is only used for io_uring descriptions, thus, enable it // only if io_uring syscalls are enabled. - "syz_memcpy_off": isSyzIoUringSupported, - "syz_btf_id_by_name": isBtfVmlinuxSupported, - "syz_fuse_handle_req": isSyzFuseSupported, + "syz_memcpy_off": isSyzIoUringSupported, + "syz_btf_id_by_name": isBtfVmlinuxSupported, + "syz_fuse_handle_req": isSyzFuseSupported, + "syz_80211_inject_frame": isWifiEmulationSupported, + "syz_80211_join_ibss": isWifiEmulationSupported, } func isSupportedSyzkall(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) { diff --git a/pkg/ipc/ipc.go b/pkg/ipc/ipc.go index 5ae502e5..947c0f68 100644 --- a/pkg/ipc/ipc.go +++ b/pkg/ipc/ipc.go @@ -41,6 +41,7 @@ const ( FlagEnableCloseFds // close fds after each program FlagEnableDevlinkPCI // setup devlink PCI device FlagEnableVhciInjection // setup and use /dev/vhci for hci packet injection + FlagEnableWifi // setup and use mac80211_hwsim for wifi emulation ) // Per-exec flags for ExecOpts.Flags. diff --git a/pkg/repro/repro.go b/pkg/repro/repro.go index 7d22c018..ec031c1c 100644 --- a/pkg/repro/repro.go +++ b/pkg/repro/repro.go @@ -202,6 +202,9 @@ func createStartOptions(cfg *mgrconfig.Config, features *host.Features, crashTyp if !features[host.FeatureVhciInjection].Enabled { opts.VhciInjection = false } + if !features[host.FeatureWifiEmulation].Enabled { + opts.Wifi = false + } } return opts } @@ -891,6 +894,7 @@ var cSimplifies = append(progSimplifies, []Simplify{ opts.DevlinkPCI = false opts.USB = false opts.VhciInjection = false + opts.Wifi = false return true }, func(opts *csource.Options) bool { @@ -958,6 +962,13 @@ var cSimplifies = append(progSimplifies, []Simplify{ opts.VhciInjection = false return true }, + func(opts *csource.Options) bool { + if !opts.Wifi { + return false + } + opts.Wifi = false + return true + }, func(opts *csource.Options) bool { if !opts.UseTmpDir || opts.Sandbox == "namespace" || opts.Cgroups { return false diff --git a/pkg/runtest/run.go b/pkg/runtest/run.go index 54d94924..2352028f 100644 --- a/pkg/runtest/run.go +++ b/pkg/runtest/run.go @@ -405,6 +405,9 @@ func (ctx *Context) createSyzTest(p *prog.Prog, sandbox string, threaded, cov bo if ctx.Features[host.FeatureVhciInjection].Enabled { cfg.Flags |= ipc.FlagEnableVhciInjection } + if ctx.Features[host.FeatureWifiEmulation].Enabled { + cfg.Flags |= ipc.FlagEnableWifi + } if ctx.Debug { cfg.Flags |= ipc.FlagDebug } @@ -440,6 +443,9 @@ func (ctx *Context) createCTest(p *prog.Prog, sandbox string, threaded bool, tim if ctx.Features[host.FeatureVhciInjection].Enabled { opts.VhciInjection = true } + if ctx.Features[host.FeatureWifiEmulation].Enabled { + opts.Wifi = true + } } src, err := csource.Write(p, opts) if err != nil { diff --git a/sys/linux/net_80211.txt b/sys/linux/net_80211.txt new file mode 100644 index 00000000..0847b8cd --- /dev/null +++ b/sys/linux/net_80211.txt @@ -0,0 +1,51 @@ +# Copyright 2020 syzkaller project authors. All rights reserved. +# Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +include + +type ieee80211_fixed_mac_addr[LAST] { + byte0 const[0x8, int8] + byte1 const[0x2, int8] + byte2 const[0x11, int8] + byte3 const[0x0, int8] + byte4 const[0x0, int8] + byte5 LAST +} [packed] + +ieee80211_mac_addr [ + device_a ieee80211_fixed_mac_addr[const[0x0, int8]] + device_b ieee80211_fixed_mac_addr[const[0x1, int8]] + broadcast array[const[0xff, int8], 6] +] + +ieee80211_ssid [ + random array[int8, 0:IEEE80211_MAX_SSID_LEN] + default_ibss_ssid array[const[0x1, int8], 6] +] [varlen] + +type ieee80211_frame array[int8] + +# Inject an 802.11 frame. +# mac_addr -- mac address of the device that will receive the message (actually it determines +# the network interface that will receive this message). +# buf -- raw 802.11 frame. It should neither include an FCS, nor leave space for it at the end of the frame. +syz_80211_inject_frame(mac_addr ptr[in, ieee80211_mac_addr], buf ptr[in, ieee80211_frame], buf_len len[buf]) + +# Pseudo system call that puts a specific interface into IBSS state and joins an IBSS network. +# Although it is done for all interfaces at executor initialization and the nl80211 commands that it executes +# are present in syzkaller descriptions of nl80211, experiments demonstrated that addition of this pseudo +# syscall provokes a much bigger number of issues. +# Also, this pseudo call makes it possible to put interfaces generated by sendmsg$NL80211_CMD_NEW_INTERFACE +# into an operable state at runtime. +syz_80211_join_ibss(interface ptr[in, string[devnames]], ssid ptr[in, ieee80211_ssid], ssid_len len[ssid], join_mode flags[join_ibss_modes]) + +# Modes of syz_80211_join_ibss operation: +# JOIN_IBSS_NO_SCAN -- channel scan is not performed and syz_80211_join_ibss waits until the interface reaches IF_OPER_UP +# JOIN_IBSS_BG_SCAN -- channel scan is performed (takes ~ 9 seconds), syz_80211_join_ibss does not await IF_OPER_UP +# JOIN_IBSS_BG_NO_SCAN -- channel scan is not performed, syz_80211_join_ibss does not await IF_OPER_UP + +define JOIN_IBSS_NO_SCAN 0x0 +define JOIN_IBSS_BG_SCAN 0x1 +define JOIN_IBSS_BG_NO_SCAN 0x2 + +join_ibss_modes = JOIN_IBSS_NO_SCAN, JOIN_IBSS_BG_SCAN, JOIN_IBSS_BG_NO_SCAN diff --git a/sys/linux/net_80211.txt.const b/sys/linux/net_80211.txt.const new file mode 100644 index 00000000..86aaf01d --- /dev/null +++ b/sys/linux/net_80211.txt.const @@ -0,0 +1,6 @@ +# Code generated by syz-sysgen. DO NOT EDIT. +arches = 386, amd64, arm, arm64, mips64le, ppc64le, riscv64, s390x +IEEE80211_MAX_SSID_LEN = 32 +JOIN_IBSS_BG_NO_SCAN = 2 +JOIN_IBSS_BG_SCAN = 1 +JOIN_IBSS_NO_SCAN = 0 diff --git a/sys/linux/socket.txt b/sys/linux/socket.txt index a8595fc5..5bc654c7 100644 --- a/sys/linux/socket.txt +++ b/sys/linux/socket.txt @@ -384,7 +384,7 @@ rtentry { # Note: lapb0, bpq0 and hwsim0 are only present in init namespace. # Note: for roseN and nrN we should use proc type, but for simplicity we currently use N=0. # Note: netdevsim0 and netpci0 are renamed in initialize_devlink_ports() -devnames = "", "lo", "tunl0", "gre0", "gretap0", "ip_vti0", "ip6_vti0", "sit0", "ip6tnl0", "ip6gre0", "ip6gretap0", "bond0", "dummy0", "nr0", "rose0", "erspan0", "vlan0", "bridge0", "vcan0", "team0", "syz_tun", "veth0", "veth1", "veth0_to_bridge", "veth1_to_bridge", "veth0_to_bond", "veth1_to_bond", "veth0_to_team", "veth1_to_team", "bridge_slave_0", "bridge_slave_1", "bond_slave_0", "bond_slave_1", "team_slave_0", "team_slave_1", "syzkaller0", "syzkaller1", "veth0_to_hsr", "veth1_to_hsr", "hsr0", "ip6erspan0", "vxcan1", "caif0", "batadv0", "veth0_to_batadv", "veth1_to_batadv", "batadv_slave_0", "batadv_slave_1", "netdevsim0", "netpci0", "xfrm0", "veth0_virt_wifi", "veth1_virt_wifi", "virt_wifi0", "veth0_vlan", "veth1_vlan", "vlan0", "vlan1", "macvlan0", "macvlan1", "ipvlan0", "ipvlan1", "veth0_macvtap", "veth1_macvtap", "macvtap0", "macsec0", "geneve0", "geneve1", "wg0", "wg1", "wg2" +devnames = "", "lo", "tunl0", "gre0", "gretap0", "ip_vti0", "ip6_vti0", "sit0", "ip6tnl0", "ip6gre0", "ip6gretap0", "bond0", "dummy0", "nr0", "rose0", "erspan0", "vlan0", "bridge0", "vcan0", "team0", "syz_tun", "veth0", "veth1", "veth0_to_bridge", "veth1_to_bridge", "veth0_to_bond", "veth1_to_bond", "veth0_to_team", "veth1_to_team", "bridge_slave_0", "bridge_slave_1", "bond_slave_0", "bond_slave_1", "team_slave_0", "team_slave_1", "syzkaller0", "syzkaller1", "veth0_to_hsr", "veth1_to_hsr", "hsr0", "ip6erspan0", "vxcan1", "caif0", "batadv0", "veth0_to_batadv", "veth1_to_batadv", "batadv_slave_0", "batadv_slave_1", "netdevsim0", "netpci0", "xfrm0", "veth0_virt_wifi", "veth1_virt_wifi", "virt_wifi0", "veth0_vlan", "veth1_vlan", "vlan0", "vlan1", "macvlan0", "macvlan1", "ipvlan0", "ipvlan1", "veth0_macvtap", "veth1_macvtap", "macvtap0", "macsec0", "geneve0", "geneve1", "wg0", "wg1", "wg2", "wlan0", "wlan1" type devname string[devnames, IFNAMSIZ] diff --git a/sys/linux/test/80211_ibss b/sys/linux/test/80211_ibss new file mode 100644 index 00000000..0bea2f5f --- /dev/null +++ b/sys/linux/test/80211_ibss @@ -0,0 +1,9 @@ +# requires: -sandbox=namespace + +# Join IBSSS network + +syz_80211_join_ibss(&AUTO='wlan0\x00', &AUTO=@default_ibss_ssid, 0x6, 0x0) + +# Inject an arbitrary packet + +syz_80211_inject_frame(&AUTO=@device_a, &AUTO="00112233445566778899", 0xa) \ No newline at end of file diff --git a/syz-fuzzer/fuzzer.go b/syz-fuzzer/fuzzer.go index ae9a5af5..16acd7a2 100644 --- a/syz-fuzzer/fuzzer.go +++ b/syz-fuzzer/fuzzer.go @@ -121,6 +121,9 @@ func createIPCConfig(features *host.Features, config *ipc.Config) { if features[host.FeatureVhciInjection].Enabled { config.Flags |= ipc.FlagEnableVhciInjection } + if features[host.FeatureWifiEmulation].Enabled { + config.Flags |= ipc.FlagEnableWifi + } } // nolint: funlen diff --git a/tools/syz-execprog/execprog.go b/tools/syz-execprog/execprog.go index a5ef8fbc..23fd881c 100644 --- a/tools/syz-execprog/execprog.go +++ b/tools/syz-execprog/execprog.go @@ -332,5 +332,8 @@ func createConfig(target *prog.Target, if featuresFlags["vhci"].Enabled && features[host.FeatureVhciInjection].Enabled { config.Flags |= ipc.FlagEnableVhciInjection } + if featuresFlags["wifi"].Enabled && features[host.FeatureWifiEmulation].Enabled { + config.Flags |= ipc.FlagEnableWifi + } return config, execOpts } diff --git a/tools/syz-prog2c/prog2c.go b/tools/syz-prog2c/prog2c.go index 4c04487f..2f8c6c62 100644 --- a/tools/syz-prog2c/prog2c.go +++ b/tools/syz-prog2c/prog2c.go @@ -92,6 +92,7 @@ func main() { DevlinkPCI: features["devlink_pci"].Enabled, USB: features["usb"].Enabled, VhciInjection: features["vhci"].Enabled, + Wifi: features["wifi"].Enabled, UseTmpDir: *flagUseTmpDir, HandleSegv: *flagHandleSegv, Repro: *flagRepro, diff --git a/tools/syz-reprolist/reprolist.go b/tools/syz-reprolist/reprolist.go index e4c26011..95c099ba 100644 --- a/tools/syz-reprolist/reprolist.go +++ b/tools/syz-reprolist/reprolist.go @@ -153,6 +153,8 @@ func createCRepro(bug *dashapi.LoadBugResp) error { return err } +// Although liter complains about this function, it does not seem complex. +// nolint: gocyclo func createProg2CArgs(bug *dashapi.LoadBugResp, opts csource.Options, file string) []string { haveEnableFlag := containsCommit("dfd609eca1871f01757d6b04b19fc273c87c14e5") haveRepeatFlag := containsCommit("b25fc7b83119e8dca728a199fd92e24dd4c33fa4") @@ -232,6 +234,10 @@ func createProg2CArgs(bug *dashapi.LoadBugResp, opts csource.Options, file strin enable = append(enable, "vhci") flags = append(flags, "-vhci") } + if opts.Wifi { + enable = append(enable, "wifi") + flags = append(flags, "-wifi") + } if !haveEnableFlag { args = append(args, flags...) } else if len(enable) != 0 { diff --git a/tools/syz-stress/stress.go b/tools/syz-stress/stress.go index 9ec0229f..21926fe8 100644 --- a/tools/syz-stress/stress.go +++ b/tools/syz-stress/stress.go @@ -165,6 +165,9 @@ func createIPCConfig(target *prog.Target, features *host.Features, featuresFlags if featuresFlags["vhci"].Enabled && features[host.FeatureVhciInjection].Enabled { config.Flags |= ipc.FlagEnableVhciInjection } + if featuresFlags["wifi"].Enabled && features[host.FeatureWifiEmulation].Enabled { + config.Flags |= ipc.FlagEnableWifi + } return config, execOpts, nil }