Writeup · mwemu kernel-mode

A beacon, a scan, and one byte of stack

The previous chapter drove a deliberately vulnerable driver inside mwemu. This one points the same rig at a real, unmodified in-tree driver — and a WPS attribute that lies about its length turns a nearby Wi-Fi frame into a write past a one-byte stack variable.

Nothing here is a knock on the kernel. Wi-Fi drivers parse hostile bytes from the air on the hot path, at enormous volume, and the people who maintain them do careful, thankless work. The point is smaller and friendlier: emulation is a cheap way to surface this class of bug early, so a human with the hardware can confirm it.

Cyberpunk illustration: the author and an AI robot ride a neon motorcycle labelled MWEMU down a highway made of binary code, past road signs reading EMULATE > EXECUTE > EXPLORE.
Human, AI and mwemu on the same bike — tooling surfaces the candidate; a person still has to ride it the rest of the way, onto real hardware.

From a toy driver to a real one

The last writeup linked a .ko into mwemu, resolved its imports to a synthetic kernel, and modelled the slab so a lifetime bug surfaced as a report instead of a panic. The natural next question: does the same rig say anything useful about code that ships to real machines? So I loaded drivers/staging/rtl8723bs — the driver for a common RTL8723BS SDIO Wi-Fi part — exactly as the tree ships it, and drove one function that parses received frames.

An attribute that lies about its length

A WPS information element is a list of attributes, each [id:2][data_len:2][data…]. rtw_get_wps_attr_content() finds one and copies its data into the caller's buffer — using the length straight from the frame, with nothing said about how big the destination is:

memcpy(buf_content, attr_ptr + 4, attr_len - 4);   /* no bound on the destination */

The Selected Registrar attribute is one byte by spec, so callers hand it a one-byte destination. And this runs on the ordinary scan path — for every network seen, rtw_cfg80211_inform_bss() reads the WPS IE out of a received beacon / probe-response into a single stack byte:

u8 sr = 0;                                          /* one byte on the stack */
...
rtw_get_wps_attr_content(wpsie, wpsielen,
                         WPS_ATTR_SELECTED_REGISTRAR, &sr, NULL);

A frame whose Selected Registrar attribute declares a length larger than one byte makes the copy run long, over the stack. The IEs come verbatim from the received frame, so the length is attacker-controlled — and a scan happens on its own under NetworkManager/iwd. No pairing, no association, just being in radio range while the machine looks around.

What mwemu saw

Driven with a crafted WPS IE (a Selected Registrar attribute of declared length 100) into a one-byte tracked chunk, the ledger flags the copy exactly where it leaves the object:

BUG: KMWEMU: slab-out-of-bounds in u8 sr of size 100 at addr 0xffff888000000001
  object 0xffff888000000000..0xffff888000000008 (requested 1 bytes, bucket 8), offset 1

Same idea as the toy driver: the destination stays mapped and guarded, so the write past it becomes a line of text instead of an invisible corruption. On a normal kernel the corrupted stack canary lands you, at minimum, in a __stack_chk_fail panic — a remote, unauthenticated denial of service for a machine with this chip that happens to be scanning nearby.

What emulation can and can't tell you

This is the honest part, and it is the whole reason the workflow is healthy. Emulation drove the parser in isolation and proved the mechanism; the reachability chain — received frame → scan queue → the one-byte destination — was read from the source. What I did not do is run the whole path end to end on a real RTL8723BS. That last step matters: analysis and LLM-assisted review are good at producing plausible memory-safety candidates, and a healthy fraction of plausible candidates are wrong. Confirming them on the actual hardware is what separates a real fix from noise, and asking for that confirmation is good stewardship, not gatekeeping — drivers/staging/ is explicitly low-assurance (TAINT_CRAP) and treated accordingly.

So this is written as what it is: a strong, tool-surfaced candidate with a one-line fix, reported upstream, waiting on someone with the part to close the loop on metal.

The fix, and the wider pattern

The fix is small — reject an attribute that claims more data than the IE holds, hand the function the destination size, and clamp the copy to it (and compute the length in a wider type so it can't wrap). Worth a broader look: this rtw_get_wps_attr* code is vendor code duplicated across the wider Realtek family, so the same shape is worth checking wherever it was copied — several out-of-tree forks carry it, and at least one already clamps.

The takeaway isn't alarm. It's that over-the-air parsers are reachable by design, the bugs in them are ordinary and fixable, and a driver emulator is a cheap early net — one that hands a finding to a human, who still has to prove it on hardware.

References

Reported to the kernel security list and the staging maintainers; a fix was posted publicly. Found by source review plus function-level emulation under mwemu, assisted by an AI, and reviewed and signed off by a human. Not yet confirmed on real hardware.