Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bd9ed86f authored by Łukasz Rymanowski's avatar Łukasz Rymanowski
Browse files

gatt: Fix possible assert on GATT timeout while ACL disconnection

This solves race when gatt is handling timeout on ATT which was sent on
EATT, but EATT channel is not existing anymore.

Bug: 284908879
Test: atest BluetoothInstrumentationTests
Tag: #feature
Change-Id: I1aa9d859269df8853d6ba03a964200625a1581ac
parent 040aed1c
Loading
Loading
Loading
Loading
+15 −5
Original line number Original line Diff line number Diff line
@@ -436,12 +436,17 @@ static tGATT_STATUS attp_cl_send_cmd(tGATT_TCB& tcb, tGATT_CLCB* p_clcb,


  if (gatt_tcb_is_cid_busy(tcb, p_clcb->cid) &&
  if (gatt_tcb_is_cid_busy(tcb, p_clcb->cid) &&
      cmd_code != GATT_HANDLE_VALUE_CONF) {
      cmd_code != GATT_HANDLE_VALUE_CONF) {
    gatt_cmd_enq(tcb, p_clcb, true, cmd_code, p_cmd);
    if (gatt_cmd_enq(tcb, p_clcb, true, cmd_code, p_cmd)) {
      LOG_DEBUG("Enqueued ATT command %p conn_id=0x%04x, cid=%d", p_clcb,
      LOG_DEBUG("Enqueued ATT command %p conn_id=0x%04x, cid=%d", p_clcb,
                p_clcb->conn_id, p_clcb->cid);
                p_clcb->conn_id, p_clcb->cid);
      return GATT_CMD_STARTED;
      return GATT_CMD_STARTED;
    }
    }


    LOG_ERROR("%s, cid 0x%02x already disconnected",
             ADDRESS_TO_LOGGABLE_CSTR(tcb.peer_bda), p_clcb->cid);
    return GATT_INTERNAL_ERROR;
  }

  LOG_DEBUG(
  LOG_DEBUG(
      "Sending ATT command to l2cap cid:0x%04x eatt_channels:%u transport:%s",
      "Sending ATT command to l2cap cid:0x%04x eatt_channels:%u transport:%s",
      p_clcb->cid, tcb.eatt, bt_transport_text(tcb.transport).c_str());
      p_clcb->cid, tcb.eatt, bt_transport_text(tcb.transport).c_str());
@@ -460,7 +465,12 @@ static tGATT_STATUS attp_cl_send_cmd(tGATT_TCB& tcb, tGATT_CLCB* p_clcb,
  LOG_DEBUG("Starting ATT response timer %p conn_id=0x%04x, cid=%d", p_clcb,
  LOG_DEBUG("Starting ATT response timer %p conn_id=0x%04x, cid=%d", p_clcb,
            p_clcb->conn_id, p_clcb->cid);
            p_clcb->conn_id, p_clcb->cid);
  gatt_start_rsp_timer(p_clcb);
  gatt_start_rsp_timer(p_clcb);
  gatt_cmd_enq(tcb, p_clcb, false, cmd_code, NULL);
  if (!gatt_cmd_enq(tcb, p_clcb, false, cmd_code, NULL)) {
    LOG_ERROR("Could not queue sent request. %s, cid 0x%02x already disconnected",
               ADDRESS_TO_LOGGABLE_CSTR(tcb.peer_bda), p_clcb->cid);
    return GATT_INTERNAL_ERROR;
  }

  return att_ret;
  return att_ret;
}
}


+1 −1
Original line number Original line Diff line number Diff line
@@ -650,7 +650,7 @@ void gatt_act_discovery(tGATT_CLCB* p_clcb);
void gatt_act_read(tGATT_CLCB* p_clcb, uint16_t offset);
void gatt_act_read(tGATT_CLCB* p_clcb, uint16_t offset);
void gatt_act_write(tGATT_CLCB* p_clcb, uint8_t sec_act);
void gatt_act_write(tGATT_CLCB* p_clcb, uint8_t sec_act);
tGATT_CLCB* gatt_cmd_dequeue(tGATT_TCB& tcb, uint16_t cid, uint8_t* p_opcode);
tGATT_CLCB* gatt_cmd_dequeue(tGATT_TCB& tcb, uint16_t cid, uint8_t* p_opcode);
void gatt_cmd_enq(tGATT_TCB& tcb, tGATT_CLCB* p_clcb, bool to_send,
bool gatt_cmd_enq(tGATT_TCB& tcb, tGATT_CLCB* p_clcb, bool to_send,
                  uint8_t op_code, BT_HDR* p_buf);
                  uint8_t op_code, BT_HDR* p_buf);
void gatt_client_handle_server_rsp(tGATT_TCB& tcb, uint16_t cid,
void gatt_client_handle_server_rsp(tGATT_TCB& tcb, uint16_t cid,
                                   uint8_t op_code, uint16_t len,
                                   uint8_t op_code, uint16_t len,
+27 −1
Original line number Original line Diff line number Diff line
@@ -64,6 +64,12 @@ uint32_t gatt_sr_enqueue_cmd(tGATT_TCB& tcb, uint16_t cid, uint8_t op_code,
  } else {
  } else {
    EattChannel* channel =
    EattChannel* channel =
        EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid);
        EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid);
    if (channel == nullptr) {
      LOG_WARN("%s, cid 0x%02x already disconnected",
               ADDRESS_TO_LOGGABLE_CSTR(tcb.peer_bda), cid);
      return 0;
    }

    p_cmd = &channel->server_outstanding_cmd_;
    p_cmd = &channel->server_outstanding_cmd_;
  }
  }


@@ -104,6 +110,11 @@ bool gatt_sr_cmd_empty(tGATT_TCB& tcb, uint16_t cid) {


  EattChannel* channel =
  EattChannel* channel =
      EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid);
      EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid);
  if (channel == nullptr) {
    LOG_WARN("%s, cid 0x%02x already disconnected",
             ADDRESS_TO_LOGGABLE_CSTR(tcb.peer_bda), cid);
    return false;
  }


  return (channel->server_outstanding_cmd_.op_code == 0);
  return (channel->server_outstanding_cmd_.op_code == 0);
}
}
@@ -125,6 +136,11 @@ void gatt_dequeue_sr_cmd(tGATT_TCB& tcb, uint16_t cid) {
  } else {
  } else {
    EattChannel* channel =
    EattChannel* channel =
        EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid);
        EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid);
    if (channel == nullptr) {
      LOG_WARN("%s, cid 0x%02x already disconnected",
               ADDRESS_TO_LOGGABLE_CSTR(tcb.peer_bda), cid);
      return;
    }


    p_cmd = &channel->server_outstanding_cmd_;
    p_cmd = &channel->server_outstanding_cmd_;
  }
  }
@@ -420,6 +436,11 @@ void gatt_process_read_multi_req(tGATT_TCB& tcb, uint16_t cid, uint8_t op_code,
  VLOG(1) << __func__;
  VLOG(1) << __func__;


  tGATT_READ_MULTI* multi_req = gatt_sr_get_read_multi(tcb, cid);
  tGATT_READ_MULTI* multi_req = gatt_sr_get_read_multi(tcb, cid);
  if (multi_req == nullptr) {
    LOG_ERROR("Could not proceed request. %s, 0x%02x",
              ADDRESS_TO_LOGGABLE_CSTR(tcb.peer_bda), cid);
    return;
  }
  multi_req->num_handles = 0;
  multi_req->num_handles = 0;
  multi_req->variable_len = (op_code == GATT_REQ_READ_MULTI_VAR);
  multi_req->variable_len = (op_code == GATT_REQ_READ_MULTI_VAR);
  gatt_sr_get_sec_info(tcb.peer_bda, tcb.transport, &sec_flag, &key_size);
  gatt_sr_get_sec_info(tcb.peer_bda, tcb.transport, &sec_flag, &key_size);
@@ -470,7 +491,12 @@ void gatt_process_read_multi_req(tGATT_TCB& tcb, uint16_t cid, uint8_t op_code,
    trans_id = gatt_sr_enqueue_cmd(tcb, cid, op_code, multi_req->handles[0]);
    trans_id = gatt_sr_enqueue_cmd(tcb, cid, op_code, multi_req->handles[0]);
    if (trans_id != 0) {
    if (trans_id != 0) {
      tGATT_SR_CMD* sr_cmd_p = gatt_sr_get_cmd_by_cid(tcb, cid);
      tGATT_SR_CMD* sr_cmd_p = gatt_sr_get_cmd_by_cid(tcb, cid);

      if (sr_cmd_p == nullptr) {
        LOG_ERROR(
            "Could not send response on CID were request arrived. %s, 0x%02x",
            ADDRESS_TO_LOGGABLE_CSTR(tcb.peer_bda), cid);
        return;
      }
      gatt_sr_reset_cback_cnt(tcb,
      gatt_sr_reset_cback_cnt(tcb,
                              cid); /* read multiple use multi_rsp_q's count*/
                              cid); /* read multiple use multi_rsp_q's count*/


+47 −4
Original line number Original line Diff line number Diff line
@@ -998,7 +998,11 @@ bool gatt_tcb_is_cid_busy(tGATT_TCB& tcb, uint16_t cid) {


  EattChannel* channel =
  EattChannel* channel =
      EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid);
      EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid);
  if (!channel) return false;
  if (channel == nullptr) {
    LOG_WARN("%s, cid 0x%02x already disconnected",
             ADDRESS_TO_LOGGABLE_CSTR(tcb.peer_bda), cid);
    return false;
  }


  return !channel->cl_cmd_q_.empty();
  return !channel->cl_cmd_q_.empty();
}
}
@@ -1138,6 +1142,11 @@ uint16_t gatt_tcb_get_payload_size(tGATT_TCB& tcb, uint16_t cid) {


  EattChannel* channel =
  EattChannel* channel =
      EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid);
      EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid);
  if (channel == nullptr) {
    LOG_WARN("%s, cid 0x%02x already disconnected",
             ADDRESS_TO_LOGGABLE_CSTR(tcb.peer_bda), cid);
    return 0;
  }


  /* ATT MTU for EATT is min from tx and rx mtu*/
  /* ATT MTU for EATT is min from tx and rx mtu*/
  return std::min<uint16_t>(channel->tx_mtu_, channel->rx_mtu_);
  return std::min<uint16_t>(channel->tx_mtu_, channel->rx_mtu_);
@@ -1197,6 +1206,8 @@ void gatt_clcb_invalidate(tGATT_TCB* p_tcb, const tGATT_CLCB* p_clcb) {
    EattChannel* channel = EattExtension::GetInstance()->FindEattChannelByCid(
    EattChannel* channel = EattExtension::GetInstance()->FindEattChannelByCid(
        p_tcb->peer_bda, cid);
        p_tcb->peer_bda, cid);
    if (channel == nullptr) {
    if (channel == nullptr) {
      LOG_WARN("%s, cid 0x%02x already disconnected",
               ADDRESS_TO_LOGGABLE_CSTR(p_tcb->peer_bda), cid);
      return;
      return;
    }
    }
    cl_cmd_q_p = &channel->cl_cmd_q_;
    cl_cmd_q_p = &channel->cl_cmd_q_;
@@ -1343,6 +1354,11 @@ void gatt_sr_reset_cback_cnt(tGATT_TCB& tcb, uint16_t cid) {
    } else {
    } else {
      EattChannel* channel =
      EattChannel* channel =
          EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid);
          EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid);
      if (channel == nullptr) {
        LOG_WARN("%s, cid 0x%02x already disconnected",
                 ADDRESS_TO_LOGGABLE_CSTR(tcb.peer_bda), cid);
        return;
      }
      channel->server_outstanding_cmd_.cback_cnt[i] = 0;
      channel->server_outstanding_cmd_.cback_cnt[i] = 0;
    }
    }
  }
  }
@@ -1373,6 +1389,12 @@ tGATT_SR_CMD* gatt_sr_get_cmd_by_cid(tGATT_TCB& tcb, uint16_t cid) {
  } else {
  } else {
    EattChannel* channel =
    EattChannel* channel =
        EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid);
        EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid);
    if (channel == nullptr) {
      LOG_WARN("%s, cid 0x%02x already disconnected",
               ADDRESS_TO_LOGGABLE_CSTR(tcb.peer_bda), cid);
      return nullptr;
    }

    sr_cmd_p = &channel->server_outstanding_cmd_;
    sr_cmd_p = &channel->server_outstanding_cmd_;
  }
  }


@@ -1389,6 +1411,11 @@ tGATT_READ_MULTI* gatt_sr_get_read_multi(tGATT_TCB& tcb, uint16_t cid) {
  } else {
  } else {
    EattChannel* channel =
    EattChannel* channel =
        EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid);
        EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid);
    if (channel == nullptr) {
      LOG_WARN("%s, cid 0x%02x already disconnected",
               ADDRESS_TO_LOGGABLE_CSTR(tcb.peer_bda), cid);
      return nullptr;
    }
    read_multi_p = &channel->server_outstanding_cmd_.multi_req;
    read_multi_p = &channel->server_outstanding_cmd_.multi_req;
  }
  }


@@ -1414,6 +1441,11 @@ void gatt_sr_update_cback_cnt(tGATT_TCB& tcb, uint16_t cid, tGATT_IF gatt_if,
  } else {
  } else {
    EattChannel* channel =
    EattChannel* channel =
        EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid);
        EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid);
    if (channel == nullptr) {
      LOG_WARN("%s, cid 0x%02x already disconnected",
               ADDRESS_TO_LOGGABLE_CSTR(tcb.peer_bda), cid);
      return;
    }
    sr_cmd_p = &channel->server_outstanding_cmd_;
    sr_cmd_p = &channel->server_outstanding_cmd_;
  }
  }


@@ -1508,7 +1540,7 @@ bool gatt_cancel_open(tGATT_IF gatt_if, const RawAddress& bda) {
}
}


/** Enqueue this command */
/** Enqueue this command */
void gatt_cmd_enq(tGATT_TCB& tcb, tGATT_CLCB* p_clcb, bool to_send,
bool gatt_cmd_enq(tGATT_TCB& tcb, tGATT_CLCB* p_clcb, bool to_send,
                  uint8_t op_code, BT_HDR* p_buf) {
                  uint8_t op_code, BT_HDR* p_buf) {
  tGATT_CMD_Q cmd;
  tGATT_CMD_Q cmd;
  cmd.to_send = to_send; /* waiting to be sent */
  cmd.to_send = to_send; /* waiting to be sent */
@@ -1522,9 +1554,15 @@ void gatt_cmd_enq(tGATT_TCB& tcb, tGATT_CLCB* p_clcb, bool to_send,
  } else {
  } else {
    EattChannel* channel =
    EattChannel* channel =
        EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cmd.cid);
        EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cmd.cid);
    CHECK(channel);
    if (channel == nullptr) {
      LOG_WARN("%s, cid 0x%02x already disconnected",
               ADDRESS_TO_LOGGABLE_CSTR(tcb.peer_bda), cmd.cid);
      return false;
    }
    channel->cl_cmd_q_.push_back(cmd);
    channel->cl_cmd_q_.push_back(cmd);
  }
  }

  return true;
}
}


/** dequeue the command in the client CCB command queue */
/** dequeue the command in the client CCB command queue */
@@ -1536,7 +1574,12 @@ tGATT_CLCB* gatt_cmd_dequeue(tGATT_TCB& tcb, uint16_t cid, uint8_t* p_op_code) {
  } else {
  } else {
    EattChannel* channel =
    EattChannel* channel =
        EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid);
        EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid);
    CHECK(channel);
    if (channel == nullptr) {
      LOG_WARN("%s, cid 0x%02x already disconnected",
               ADDRESS_TO_LOGGABLE_CSTR(tcb.peer_bda), cid);
      return nullptr;
    }

    cl_cmd_q_p = &channel->cl_cmd_q_;
    cl_cmd_q_p = &channel->cl_cmd_q_;
  }
  }