Compare commits

...

10 Commits

Author SHA1 Message Date
James Prestwood c2ad0006eb ap: bail in ap_del_station if AP is going down
Caught by static analysis, if this condition is met the AP is going
down so we cannot continue further accessing the ap object.
2024-02-29 14:37:49 -06:00
James Prestwood 5fcfb430b2 ap: verify ATTR_MAC exists in NEW_STATION
Caught by static analysis, if ATTR_MAC was not in the message there
would be a memcpy with uninitialized bytes. In addition there is no
reason to memcpy twice. Instead 'mac' can be a const pointer which
both verifies it exists and removes the need for a second memcpy.
2024-02-29 14:37:42 -06:00
James Prestwood 3a17c8e3c5 nl80211util: check l_genl_attr_recurse return in extract_nested
Caught by static analysis, the recurse operation return was not being
checked.
2024-02-29 14:37:29 -06:00
James Prestwood 6febe5bed1 ap: allow va_end to get called in ap_handshake_event
Caught by static analysis, va_end was never being called since the
REKEY_COMPLETE event was returning early.
2024-02-29 14:37:19 -06:00
James Prestwood 338577bb3a ap: check that the last band_freq_attrs was set
Static analysis complains that 'last' could be NULL which is true.
This really could only happen if every frequency was disabled which
likely is impossible but in any case, check before dereferencing
the pointer.
2024-02-29 14:37:01 -06:00
James Prestwood 83c032a583 p2putil: check length of client info description
A length check was missing which could cause a out of bounds read.

Co-authored-by: Alex Radocea <alex@supernetworks.org>
2024-02-29 14:36:16 -06:00
James Prestwood d34b4e16e0 p2putil: initialize all parsing structures to zero
Since these are all stack variables they are not zero initialized.
If parsing fails there may be invalid pointers within the structures
which can get dereferenced by p2p_clear_*
2024-02-29 14:33:23 -06:00
James Prestwood 52a47c9fd4 p2putil: fix crash/remove side effect parsing adv service info
The input queue pointer was being initialized unconditionally so if
parsing fails the out pointer is still set after the queue is
destroyed. This causes a crash during cleanup.

Instead use a temporary pointer while parsing and only after parsing
has finished do we set the out pointer.

Reported-By: Alex Radocea <alex@supernetworks.org>
2024-02-29 14:32:53 -06:00
James Prestwood 816d258cab auto-t: Add frame fuzzing test
Add a test to validate a crash found by Alex Radocea when sending
a fuzzed beacon frame.

Co-authored-by: Alex Radocea <alex@supernetworks.org>
2024-02-29 14:32:33 -06:00
James Prestwood 5067654a6d auto-t: end process_io on HUP signal, detect process crash
When HUP is received the IO read callback was never completing which
caused it to block indefinitely until waited for. This didn't matter
for most transient processes but for IWD, hostapd, wpa_supplicant
it would cause test-runner to hang if the process crashed.

Detecting a crash is somewhat hacky because we have no process
management like systemd and the return code isn't reliable as some
processes return non-zero under normal circumstances. So to detect
a crash the process output is being checked for the string:
"++++++++ backtrace ++++++++". This isn't 100% reliable obviously
since its dependent on how the binary is compiled, but even if the
crash itself isn't detected any test should still fail if written
correctly.

Doing this allows auto-tests to handle IWD crashes gracefully by
failing the test, printing the exception (event without debugging)
and continue with other tests.
2024-02-29 14:31:55 -06:00
7 changed files with 162 additions and 35 deletions

View File

@ -0,0 +1,72 @@
import unittest
import sys
import sys
import os
from scapy.layers.dot11 import *
from scapy.arch import str2mac, get_if_raw_hwaddr
from time import time, sleep
from threading import Thread
def if_hwaddr(iff):
return str2mac(get_if_raw_hwaddr(iff)[1])
def config_mon(iface, channel):
"""set the interface in monitor mode and then change channel using iw"""
os.system("ip link set dev %s down" % iface)
os.system("iw dev %s set type monitor" % iface)
os.system("ip link set dev %s up" % iface)
os.system("iw dev %s set channel %d" % (iface, channel))
class AP:
def __init__(self, ssid, psk, mac=None, mode="stdio", iface="wlan0", channel=1):
self.channel = channel
self.iface = iface
self.mode = mode
if self.mode == "iface":
if not mac:
mac = if_hwaddr(iface)
config_mon(iface, channel)
if not mac:
raise Exception("Need a mac")
else:
self.mac = mac
self.boottime = time()
def get_radiotap_header(self):
return RadioTap()
def dot11_beacon(self, contents):
evil_packet = (
self.get_radiotap_header()
/ Dot11(
subtype=8, addr1="ff:ff:ff:ff:ff:ff", addr2=self.mac, addr3=self.mac
)
/ Dot11Beacon(cap=0x3101)
/ contents
)
self.sendp(evil_packet)
def run(self, contents):
interval = 0.05
num_beacons = 100
while num_beacons:
self.dot11_beacon(contents)
sleep(interval)
num_beacons -= 1
def start(self, contents):
self.thread = Thread(target=self.run, args=(contents,))
self.thread.start()
def stop(self):
self.thread.join()
def sendp(self, packet, verbose=False):
if self.mode == "stdio":
x = packet.build()
sys.stdout.buffer.write(struct.pack("<L", len(x)) + x)
sys.stdout.buffer.flush()
return
assert self.mode == "iface"
sendp(packet, iface=self.iface, verbose=False)

View File

@ -0,0 +1,7 @@
[SETUP]
num_radios=2
start_iwd=0
hwsim_medium=yes
[rad1]
reserve=true

View File

@ -0,0 +1,37 @@
#! /usr/bin/python3
import unittest
import sys
import sys
import os
from fake_ap import AP
sys.path.append('../util')
from iwd import IWD
# Probe frame that causes IWD to crash
beacon=b'\xdd\nPo\x9a\t\x0e\x00\x00\x19\x10\x00\xdd/Po\x9a\t\x0c\x02\x00\x00\xdd\x05\x03\x03\x03Po\x9a\x10\x00\x0b\x05\x0e\x00\x00\x00\x00\x0b\x05\x00\x00\x00\xdd\x05\x00\x03\x03\x03\x03\x00\x00\x00\xdd\x05\x03\x03\x03\x03\x03'
class Test(unittest.TestCase):
def test_beacon_crash(self):
wd = IWD(True)
devs = wd.list_devices()
self.assertEqual(len(devs), 1)
devs[0].autoconnect = True
os.system("iw phy rad1 interface add wlan1 type managed")
ap = AP("evilAP", "password1234", mode="iface", iface="wlan1", channel=4)
ap.start(beacon)
condition = "obj.scanning == True"
wd.wait_for_object_condition(devs[0], condition)
condition = "obj.scanning == False"
wd.wait_for_object_condition(devs[0], condition)
if __name__ == '__main__':
unittest.main(exit=True)

View File

@ -455,7 +455,8 @@ static void ap_del_station(struct sta_state *sta, uint16_t reason,
sta->ip_alloc_lease = NULL;
l_dhcp_server_expire_by_mac(ap->netconfig_dhcp, sta->addr);
ap_event_done(ap, prev);
if (ap_event_done(ap, prev))
return;
}
/*
@ -1247,8 +1248,10 @@ static size_t ap_build_country_ie(struct ap_state *ap, uint8_t *out_buf,
}
/* finish final group */
*pos++ = nchans;
*pos++ = last->tx_power;
if (last) {
*pos++ = nchans;
*pos++ = last->tx_power;
}
len = pos - out_buf - 2;
@ -1482,7 +1485,7 @@ static void ap_handshake_event(struct handshake_state *hs,
}
case HANDSHAKE_EVENT_REKEY_COMPLETE:
ap_set_sta_rekey_timer(ap, sta);
return;
break;
default:
break;
}
@ -2961,7 +2964,7 @@ static void ap_handle_new_station(struct ap_state *ap, struct l_genl_msg *msg)
uint16_t type;
uint16_t len;
const void *data;
uint8_t mac[6];
const uint8_t *mac = NULL;
uint8_t *assoc_rsne = NULL;
if (!l_genl_attr_init(&attr, msg))
@ -2981,12 +2984,12 @@ static void ap_handle_new_station(struct ap_state *ap, struct l_genl_msg *msg)
if (len != 6)
goto cleanup;
memcpy(mac, data, 6);
mac = data;
break;
}
}
if (!assoc_rsne)
if (!assoc_rsne || !mac)
goto cleanup;
/*

View File

@ -136,9 +136,7 @@ static bool extract_nested(const void *data, uint16_t len, void *o)
const struct l_genl_attr *outer = data;
struct l_genl_attr *nested = o;
l_genl_attr_recurse(outer, nested);
return true;
return l_genl_attr_recurse(outer, nested);
}
static bool extract_u8(const void *data, uint16_t len, void *o)

View File

@ -376,6 +376,9 @@ static bool extract_p2p_group_info(const uint8_t *attr, size_t len,
desc = l_new(struct p2p_client_info_descriptor, 1);
l_queue_push_tail(*out, desc);
if (desc_len < 24)
goto error;
memcpy(desc->device_addr, attr + 0, 6);
memcpy(desc->interface_addr, attr + 6, 6);
desc->device_caps = attr[12];
@ -541,7 +544,8 @@ static void p2p_clear_advertised_service_descriptor(void *data)
static bool extract_p2p_advertised_service_info(const uint8_t *attr, size_t len,
void *data)
{
struct l_queue **out = data;
struct l_queue **q = data;
struct l_queue *out = NULL;
while (len) {
struct p2p_advertised_service_descriptor *desc;
@ -557,11 +561,11 @@ static bool extract_p2p_advertised_service_info(const uint8_t *attr, size_t len,
if (!l_utf8_validate((const char *) attr + 7, name_len, NULL))
goto error;
if (!*out)
*out = l_queue_new();
if (!out)
out = l_queue_new();
desc = l_new(struct p2p_advertised_service_descriptor, 1);
l_queue_push_tail(*out, desc);
l_queue_push_tail(out, desc);
desc->advertisement_id = l_get_le32(attr + 0);
desc->wsc_config_methods = l_get_be16(attr + 4);
@ -572,10 +576,12 @@ static bool extract_p2p_advertised_service_info(const uint8_t *attr, size_t len,
len -= 7 + name_len;
}
*q = out;
return true;
error:
l_queue_destroy(*out, p2p_clear_advertised_service_descriptor);
l_queue_destroy(out, p2p_clear_advertised_service_descriptor);
return false;
}
@ -773,7 +779,7 @@ done:
/* Section 4.2.1 */
int p2p_parse_beacon(const uint8_t *pdu, size_t len, struct p2p_beacon *out)
{
struct p2p_beacon d = {};
struct p2p_beacon d = {0};
int r;
r = p2p_parse_attrs(pdu, len,
@ -794,7 +800,7 @@ int p2p_parse_beacon(const uint8_t *pdu, size_t len, struct p2p_beacon *out)
int p2p_parse_probe_req(const uint8_t *pdu, size_t len,
struct p2p_probe_req *out)
{
struct p2p_probe_req d = {};
struct p2p_probe_req d = {0};
int r;
r = p2p_parse_attrs(pdu, len,
@ -825,7 +831,7 @@ int p2p_parse_probe_req(const uint8_t *pdu, size_t len,
int p2p_parse_probe_resp(const uint8_t *pdu, size_t len,
struct p2p_probe_resp *out)
{
struct p2p_probe_resp d = {};
struct p2p_probe_resp d = {0};
int r;
r = p2p_parse_attrs(pdu, len,
@ -850,7 +856,7 @@ int p2p_parse_probe_resp(const uint8_t *pdu, size_t len,
int p2p_parse_association_req(const uint8_t *pdu, size_t len,
struct p2p_association_req *out)
{
struct p2p_association_req d = {};
struct p2p_association_req d = {0};
int r;
r = p2p_parse_attrs(pdu, len,
@ -873,7 +879,7 @@ int p2p_parse_association_req(const uint8_t *pdu, size_t len,
int p2p_parse_association_resp(const uint8_t *pdu, size_t len,
struct p2p_association_resp *out)
{
struct p2p_association_resp d = {};
struct p2p_association_resp d = {0};
int r;
r = p2p_parse_attrs(pdu, len,
@ -936,7 +942,7 @@ int p2p_parse_disassociation(const uint8_t *pdu, size_t len,
int p2p_parse_go_negotiation_req(const uint8_t *pdu, size_t len,
struct p2p_go_negotiation_req *out)
{
struct p2p_go_negotiation_req d = {};
struct p2p_go_negotiation_req d = {0};
int r;
struct p2p_go_intent_attr go_intent;
uint8_t *wsc_data;
@ -998,7 +1004,7 @@ error:
int p2p_parse_go_negotiation_resp(const uint8_t *pdu, size_t len,
struct p2p_go_negotiation_resp *out)
{
struct p2p_go_negotiation_resp d = {};
struct p2p_go_negotiation_resp d = {0};
int r;
struct p2p_go_intent_attr go_intent;
uint8_t *wsc_data;
@ -1059,7 +1065,7 @@ error:
int p2p_parse_go_negotiation_confirmation(const uint8_t *pdu, size_t len,
struct p2p_go_negotiation_confirmation *out)
{
struct p2p_go_negotiation_confirmation d = {};
struct p2p_go_negotiation_confirmation d = {0};
int r;
if (len < 1)
@ -1093,7 +1099,7 @@ error:
int p2p_parse_invitation_req(const uint8_t *pdu, size_t len,
struct p2p_invitation_req *out)
{
struct p2p_invitation_req d = {};
struct p2p_invitation_req d = {0};
int r;
uint8_t *wsc_data;
ssize_t wsc_len;
@ -1148,7 +1154,7 @@ error:
int p2p_parse_invitation_resp(const uint8_t *pdu, size_t len,
struct p2p_invitation_resp *out)
{
struct p2p_invitation_resp d = {};
struct p2p_invitation_resp d = {0};
int r;
if (len < 1)
@ -1182,7 +1188,7 @@ error:
int p2p_parse_device_disc_req(const uint8_t *pdu, size_t len,
struct p2p_device_discoverability_req *out)
{
struct p2p_device_discoverability_req d = {};
struct p2p_device_discoverability_req d = {0};
int r;
if (len < 1)
@ -1207,7 +1213,7 @@ int p2p_parse_device_disc_req(const uint8_t *pdu, size_t len,
int p2p_parse_device_disc_resp(const uint8_t *pdu, size_t len,
struct p2p_device_discoverability_resp *out)
{
struct p2p_device_discoverability_resp d = {};
struct p2p_device_discoverability_resp d = {0};
int r;
if (len < 1)
@ -1231,7 +1237,7 @@ int p2p_parse_device_disc_resp(const uint8_t *pdu, size_t len,
int p2p_parse_provision_disc_req(const uint8_t *pdu, size_t len,
struct p2p_provision_discovery_req *out)
{
struct p2p_provision_discovery_req d = {};
struct p2p_provision_discovery_req d = {0};
int r;
uint8_t *wsc_data;
ssize_t wsc_len;
@ -1306,7 +1312,7 @@ error:
int p2p_parse_provision_disc_resp(const uint8_t *pdu, size_t len,
struct p2p_provision_discovery_resp *out)
{
struct p2p_provision_discovery_resp d = {};
struct p2p_provision_discovery_resp d = {0};
int r;
uint8_t *wsc_data;
ssize_t wsc_len;
@ -1386,7 +1392,7 @@ error:
int p2p_parse_notice_of_absence(const uint8_t *pdu, size_t len,
struct p2p_notice_of_absence *out)
{
struct p2p_notice_of_absence d = {};
struct p2p_notice_of_absence d = {0};
int r;
if (len < 1)
@ -1408,7 +1414,7 @@ int p2p_parse_notice_of_absence(const uint8_t *pdu, size_t len,
int p2p_parse_presence_req(const uint8_t *pdu, size_t len,
struct p2p_presence_req *out)
{
struct p2p_presence_req d = {};
struct p2p_presence_req d = {0};
int r;
if (len < 1)
@ -1434,7 +1440,7 @@ int p2p_parse_presence_req(const uint8_t *pdu, size_t len,
int p2p_parse_presence_resp(const uint8_t *pdu, size_t len,
struct p2p_presence_resp *out)
{
struct p2p_presence_resp d = {};
struct p2p_presence_resp d = {0};
int r;
if (len < 1)

View File

@ -175,23 +175,27 @@ class Process(subprocess.Popen):
def process_io(self, source, condition):
if condition & GLib.IO_HUP:
self.hup = True
self.wait()
bt = self.out.partition("++++++++ backtrace ++++++++")
if bt[1]:
raise Exception(f"Process {self.args[0]} crashed!\n{bt[1] + bt[2]}")
data = source.read()
if not data:
return True
return not self.hup
try:
data = data.decode('utf-8')
except:
return True
return not self.hup
# Save data away in case the caller needs it (e.g. list_sta)
self.out += data
self._write_io(self, data)
return True
return not self.hup
def _append_outfile(self, file, append=True):
gid = int(os.environ.get('SUDO_GID', os.getgid()))