[PATCH] tests: add a test to check various MLD AP startup combination

Benjamin Berg benjamin at sipsolutions.net
Fri Jul 18 03:04:20 PDT 2025


From: Benjamin Berg <benjamin.berg at intel.com>

This adds a test that tries to start an MLD AP multiple times, failing
it either in different locations or not at all.

---

Hi,

This test seems triggers various existing problems in hostapd. The
failures I see are:
 1. With the current fail order, hostapd crashes on the second try
 2. If fail_pos=2 is run, then hostapd leaks memory
 3. When just running fail_pos=0 twice, the second startup fails

Note that I am not sure about #3, because I am not certain that the
link_remove() calls are actually correct.

Either way, it seems obvious that there are some problems in hostapd
currently. It would be nice if someone could look into these.

Thanks,
Benjamin

Signed-off-by: Benjamin Berg <benjamin.berg at intel.com>
---
 hostapd/ctrl_iface.c       | 12 +++++++++++
 src/ap/hostapd.c           |  4 ++++
 tests/hwsim/test_ap_eht.py | 42 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 58 insertions(+)
 create mode 100644 tests/hwsim/test_ap_eht.py

diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index 3b410ac77a..816e871e14 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -5914,6 +5914,18 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx,
 			interfaces, buf + 10, reply, reply_size);
 	} else if (os_strcmp(buf, "TERMINATE") == 0) {
 		eloop_terminate();
+#ifdef CONFIG_TESTING_OPTIONS
+	} else if (os_strncmp(buf, "TEST_ALLOC_FAIL ", 16) == 0) {
+		if (testing_set_fail_pattern(true, buf + 16) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) {
+		reply_len = testing_get_fail_pattern(true, reply, reply_size);
+	} else if (os_strncmp(buf, "TEST_FAIL ", 10) == 0) {
+		if (testing_set_fail_pattern(false, buf + 10) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "GET_FAIL") == 0) {
+		reply_len = testing_get_fail_pattern(false, reply, reply_size);
+#endif /* CONFIG_TESTING_OPTIONS */
 	} else {
 		wpa_printf(MSG_DEBUG, "Unrecognized global ctrl_iface command "
 			   "ignored");
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index f2f0c29493..ad101b9187 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -2828,6 +2828,10 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
 	unsigned int i;
 	int not_ready_in_sync_ifaces = 0;
 
+	if (TEST_FAIL_TAG(hapd->conf->iface)) {
+		err = -1;
+	}
+
 	if (!iface->need_to_start_in_sync)
 		return hostapd_setup_interface_complete_sync(iface, err);
 
diff --git a/tests/hwsim/test_ap_eht.py b/tests/hwsim/test_ap_eht.py
new file mode 100644
index 0000000000..543e9c1d2c
--- /dev/null
+++ b/tests/hwsim/test_ap_eht.py
@@ -0,0 +1,42 @@
+# EHT AP tests
+# Copyright (c) 2025, Intel Corporation
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from utils import *
+from hwsim import HWSimRadio
+from hostapd import HostapdGlobal
+from test_eht import eht_mld_enable_ap, eht_mld_ap_wpa2_params
+
+def test_mld_ap_start_fail(dev, apdev):
+    """Check various MLD AP startup combinations and failures"""
+
+    hapd_global = HostapdGlobal(apdev)
+
+    with HWSimRadio(use_mlo=True) as (hapd0_radio, hapd0_iface):
+
+        # Try two clean runs last
+        for fail_pos in (1, 2, 0, 0):
+            logger.info("Failing pos: %d", fail_pos)
+            with fail_test(hapd_global, fail_pos,
+                           f"{hapd0_iface};hostapd_setup_interface_complete"):
+                ssid = "mld_ap_owe"
+                params = eht_mld_ap_wpa2_params(ssid, key_mgmt="OWE", mfp="2")
+
+                hapd0_0 = None
+                hapd0_1 = None
+
+                try:
+                    hapd0_0 = eht_mld_enable_ap(hapd0_iface, 0, params)
+                    params['channel'] = '6'
+                    hapd0_1 = eht_mld_enable_ap(hapd0_iface, 1, params)
+                except:
+                    # We expect a failure (but not on the last run)
+                    assert fail_pos != 0, "Expected success without injected failure"
+                finally:
+                    if hapd0_0 is not None:
+                        hapd0_0.link_remove()
+                    if hapd0_1 is not None:
+                        hapd0_1.link_remove()
+
-- 
2.50.0




More information about the Hostap mailing list