Skip to content
Permalink
Browse files Browse the repository at this point in the history
shared/att: Fix possible crash on disconnect
If there are pending request while disconnecting they would be notified
but clients may endup being freed in the proccess which will then be
calling bt_att_cancel to cancal its requests causing the following
trace:

Invalid read of size 4
   at 0x1D894C: enable_ccc_callback (gatt-client.c:1627)
   by 0x1D247B: disc_att_send_op (att.c:417)
   by 0x1CCC17: queue_remove_all (queue.c:354)
   by 0x1D47B7: disconnect_cb (att.c:635)
   by 0x1E0707: watch_callback (io-glib.c:170)
   by 0x48E963B: g_main_context_dispatch (in /usr/lib/libglib-2.0.so.0.6400.4)
   by 0x48E9AC7: ??? (in /usr/lib/libglib-2.0.so.0.6400.4)
   by 0x48E9ECF: g_main_loop_run (in /usr/lib/libglib-2.0.so.0.6400.4)
   by 0x1E0E97: mainloop_run (mainloop-glib.c:79)
   by 0x1E13B3: mainloop_run_with_signal (mainloop-notify.c:201)
   by 0x12BC3B: main (main.c:770)
 Address 0x7d40a28 is 24 bytes inside a block of size 32 free'd
   at 0x484A2E0: free (vg_replace_malloc.c:540)
   by 0x1CCC17: queue_remove_all (queue.c:354)
   by 0x1CCC83: queue_destroy (queue.c:73)
   by 0x1D7DD7: bt_gatt_client_free (gatt-client.c:2209)
   by 0x16497B: batt_free (battery.c:77)
   by 0x16497B: batt_remove (battery.c:286)
   by 0x1A0013: service_remove (service.c:176)
   by 0x1A9B7B: device_remove_gatt_service (device.c:3691)
   by 0x1A9B7B: gatt_service_removed (device.c:3805)
   by 0x1CC90B: queue_foreach (queue.c:220)
   by 0x1DE27B: notify_service_changed.isra.0.part.0 (gatt-db.c:369)
   by 0x1DE387: notify_service_changed (gatt-db.c:361)
   by 0x1DE387: gatt_db_service_destroy (gatt-db.c:385)
   by 0x1DE3EF: gatt_db_remove_service (gatt-db.c:519)
   by 0x1D674F: discovery_op_complete (gatt-client.c:388)
   by 0x1D6877: discover_primary_cb (gatt-client.c:1260)
   by 0x1E220B: discovery_op_complete (gatt-helpers.c:628)
   by 0x1E249B: read_by_grp_type_cb (gatt-helpers.c:730)
   by 0x1D247B: disc_att_send_op (att.c:417)
   by 0x1CCC17: queue_remove_all (queue.c:354)
   by 0x1D47B7: disconnect_cb (att.c:635)
  • Loading branch information
Vudentz committed Jul 16, 2020
1 parent 1d1577c commit 1cd644d
Showing 1 changed file with 40 additions and 6 deletions.
46 changes: 40 additions & 6 deletions src/shared/att.c
Expand Up @@ -84,6 +84,7 @@ struct bt_att {
struct queue *req_queue; /* Queued ATT protocol requests */
struct queue *ind_queue; /* Queued ATT protocol indications */
struct queue *write_queue; /* Queue of PDUs ready to send */
bool in_disc; /* Cleanup queues on disconnect_cb */

bt_att_timeout_func_t timeout_callback;
bt_att_destroy_func_t timeout_destroy;
Expand Down Expand Up @@ -222,8 +223,10 @@ static void destroy_att_send_op(void *data)
free(op);
}

static void cancel_att_send_op(struct att_send_op *op)
static void cancel_att_send_op(void *data)
{
struct att_send_op *op = data;

if (op->destroy)
op->destroy(op->user_data);

Expand Down Expand Up @@ -631,11 +634,6 @@ static bool disconnect_cb(struct io *io, void *user_data)
/* Dettach channel */
queue_remove(att->chans, chan);

/* Notify request callbacks */
queue_remove_all(att->req_queue, NULL, NULL, disc_att_send_op);
queue_remove_all(att->ind_queue, NULL, NULL, disc_att_send_op);
queue_remove_all(att->write_queue, NULL, NULL, disc_att_send_op);

if (chan->pending_req) {
disc_att_send_op(chan->pending_req);
chan->pending_req = NULL;
Expand All @@ -654,6 +652,15 @@ static bool disconnect_cb(struct io *io, void *user_data)

bt_att_ref(att);

att->in_disc = true;

/* Notify request callbacks */
queue_remove_all(att->req_queue, NULL, NULL, disc_att_send_op);
queue_remove_all(att->ind_queue, NULL, NULL, disc_att_send_op);
queue_remove_all(att->write_queue, NULL, NULL, disc_att_send_op);

att->in_disc = false;

queue_foreach(att->disconn_list, disconn_handler, INT_TO_PTR(err));

bt_att_unregister_all(att);
Expand Down Expand Up @@ -1574,6 +1581,30 @@ bool bt_att_chan_cancel(struct bt_att_chan *chan, unsigned int id)
return true;
}

static bool bt_att_disc_cancel(struct bt_att *att, unsigned int id)
{
struct att_send_op *op;

op = queue_find(att->req_queue, match_op_id, UINT_TO_PTR(id));
if (op)
goto done;

op = queue_find(att->ind_queue, match_op_id, UINT_TO_PTR(id));
if (op)
goto done;

op = queue_find(att->write_queue, match_op_id, UINT_TO_PTR(id));

done:
if (!op)
return false;

/* Just cancel since disconnect_cb will be cleaning up */
cancel_att_send_op(op);

return true;
}

bool bt_att_cancel(struct bt_att *att, unsigned int id)
{
const struct queue_entry *entry;
Expand All @@ -1591,6 +1622,9 @@ bool bt_att_cancel(struct bt_att *att, unsigned int id)
return true;
}

if (att->in_disc)
return bt_att_disc_cancel(att, id);

op = queue_remove_if(att->req_queue, match_op_id, UINT_TO_PTR(id));
if (op)
goto done;
Expand Down

0 comments on commit 1cd644d

Please sign in to comment.