Blame SOURCES/0001-shared-att-Fix-possible-crash-on-disconnect.patch

dd55c8
From b61877eb3e05b9b9dff36b4eccc46c539634cf15 Mon Sep 17 00:00:00 2001
dd55c8
From: Gopal Tiwari <gtiwari@redhat.com>
dd55c8
Date: Thu, 22 Oct 2020 11:23:00 +0530
dd55c8
Subject: [PATCH BlueZ]     shared/att: Fix possible crash on disconnect
dd55c8
dd55c8
commit 1cd644db8c23a2f530ddb93cebed7dacc5f5721a
dd55c8
Author: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
dd55c8
Date:   Wed Jul 15 18:25:37 2020 -0700
dd55c8
dd55c8
    shared/att: Fix possible crash on disconnect
dd55c8
dd55c8
    If there are pending request while disconnecting they would be notified
dd55c8
    but clients may endup being freed in the proccess which will then be
dd55c8
    calling bt_att_cancel to cancal its requests causing the following
dd55c8
    trace:
dd55c8
dd55c8
    Invalid read of size 4
dd55c8
       at 0x1D894C: enable_ccc_callback (gatt-client.c:1627)
dd55c8
       by 0x1D247B: disc_att_send_op (att.c:417)
dd55c8
       by 0x1CCC17: queue_remove_all (queue.c:354)
dd55c8
       by 0x1D47B7: disconnect_cb (att.c:635)
dd55c8
       by 0x1E0707: watch_callback (io-glib.c:170)
dd55c8
       by 0x48E963B: g_main_context_dispatch (in /usr/lib/libglib-2.0.so.0.6400.4)
dd55c8
       by 0x48E9AC7: ??? (in /usr/lib/libglib-2.0.so.0.6400.4)
dd55c8
       by 0x48E9ECF: g_main_loop_run (in /usr/lib/libglib-2.0.so.0.6400.4)
dd55c8
       by 0x1E0E97: mainloop_run (mainloop-glib.c:79)
dd55c8
       by 0x1E13B3: mainloop_run_with_signal (mainloop-notify.c:201)
dd55c8
       by 0x12BC3B: main (main.c:770)
dd55c8
     Address 0x7d40a28 is 24 bytes inside a block of size 32 free'd
dd55c8
       at 0x484A2E0: free (vg_replace_malloc.c:540)
dd55c8
       by 0x1CCC17: queue_remove_all (queue.c:354)
dd55c8
       by 0x1CCC83: queue_destroy (queue.c:73)
dd55c8
       by 0x1D7DD7: bt_gatt_client_free (gatt-client.c:2209)
dd55c8
       by 0x16497B: batt_free (battery.c:77)
dd55c8
       by 0x16497B: batt_remove (battery.c:286)
dd55c8
       by 0x1A0013: service_remove (service.c:176)
dd55c8
     by 0x1A9B7B: device_remove_gatt_service (device.c:3691)
dd55c8
       by 0x1A9B7B: gatt_service_removed (device.c:3805)
dd55c8
       by 0x1CC90B: queue_foreach (queue.c:220)
dd55c8
       by 0x1DE27B: notify_service_changed.isra.0.part.0 (gatt-db.c:369)
dd55c8
       by 0x1DE387: notify_service_changed (gatt-db.c:361)
dd55c8
       by 0x1DE387: gatt_db_service_destroy (gatt-db.c:385)
dd55c8
       by 0x1DE3EF: gatt_db_remove_service (gatt-db.c:519)
dd55c8
       by 0x1D674F: discovery_op_complete (gatt-client.c:388)
dd55c8
       by 0x1D6877: discover_primary_cb (gatt-client.c:1260)
dd55c8
       by 0x1E220B: discovery_op_complete (gatt-helpers.c:628)
dd55c8
       by 0x1E249B: read_by_grp_type_cb (gatt-helpers.c:730)
dd55c8
       by 0x1D247B: disc_att_send_op (att.c:417)
dd55c8
       by 0x1CCC17: queue_remove_all (queue.c:354)
dd55c8
       by 0x1D47B7: disconnect_cb (att.c:635)
dd55c8
---
dd55c8
 src/shared/att.c | 46 ++++++++++++++++++++++++++++++++++++++++------
dd55c8
 1 file changed, 40 insertions(+), 6 deletions(-)
dd55c8
dd55c8
diff --git a/src/shared/att.c b/src/shared/att.c
dd55c8
index 0ea6d55bd..b0fdb8e9f 100644
dd55c8
--- a/src/shared/att.c
dd55c8
+++ b/src/shared/att.c
dd55c8
@@ -62,6 +62,7 @@ struct bt_att {
dd55c8
 	struct queue *ind_queue;	/* Queued ATT protocol indications */
dd55c8
 	struct att_send_op *pending_ind;
dd55c8
 	struct queue *write_queue;	/* Queue of PDUs ready to send */
dd55c8
+	bool in_disc;			/* Cleanup queues on disconnect_cb */
dd55c8
 	bool writer_active;
dd55c8
 
dd55c8
 	struct queue *notify_list;	/* List of registered callbacks */
dd55c8
@@ -211,8 +212,10 @@ static void destroy_att_send_op(void *data)
dd55c8
 	free(op);
dd55c8
 }
dd55c8
 
dd55c8
-static void cancel_att_send_op(struct att_send_op *op)
dd55c8
+static void cancel_att_send_op(void *data)
dd55c8
 {
dd55c8
+	struct att_send_op *op = data;
dd55c8
+
dd55c8
 	if (op->destroy)
dd55c8
 		op->destroy(op->user_data);
dd55c8
 
dd55c8
@@ -572,11 +575,6 @@ static bool disconnect_cb(struct io *io, void *user_data)
dd55c8
 	att->io = NULL;
dd55c8
 	att->fd = -1;
dd55c8
 
dd55c8
-	/* Notify request callbacks */
dd55c8
-	queue_remove_all(att->req_queue, NULL, NULL, disc_att_send_op);
dd55c8
-	queue_remove_all(att->ind_queue, NULL, NULL, disc_att_send_op);
dd55c8
-	queue_remove_all(att->write_queue, NULL, NULL, disc_att_send_op);
dd55c8
-
dd55c8
 	if (att->pending_req) {
dd55c8
 		disc_att_send_op(att->pending_req);
dd55c8
 		att->pending_req = NULL;
dd55c8
@@ -589,6 +587,15 @@ static bool disconnect_cb(struct io *io, void *user_data)
dd55c8
 
dd55c8
 	bt_att_ref(att);
dd55c8
 
dd55c8
+	att->in_disc = true;
dd55c8
+
dd55c8
+	/* Notify request callbacks */
dd55c8
+	queue_remove_all(att->req_queue, NULL, NULL, disc_att_send_op);
dd55c8
+	queue_remove_all(att->ind_queue, NULL, NULL, disc_att_send_op);
dd55c8
+	queue_remove_all(att->write_queue, NULL, NULL, disc_att_send_op);
dd55c8
+
dd55c8
+	att->in_disc = false;
dd55c8
+
dd55c8
 	queue_foreach(att->disconn_list, disconn_handler, INT_TO_PTR(err));
dd55c8
 
dd55c8
 	bt_att_unregister_all(att);
dd55c8
@@ -1306,6 +1313,30 @@ static bool match_op_id(const void *a, const void *b)
dd55c8
 	return op->id == id;
dd55c8
 }
dd55c8
 
dd55c8
+static bool bt_att_disc_cancel(struct bt_att *att, unsigned int id)
dd55c8
+{
dd55c8
+	struct att_send_op *op;
dd55c8
+
dd55c8
+	op = queue_find(att->req_queue, match_op_id, UINT_TO_PTR(id));
dd55c8
+	if (op)
dd55c8
+		goto done;
dd55c8
+
dd55c8
+	op = queue_find(att->ind_queue, match_op_id, UINT_TO_PTR(id));
dd55c8
+	if (op)
dd55c8
+		goto done;
dd55c8
+
dd55c8
+	op = queue_find(att->write_queue, match_op_id, UINT_TO_PTR(id));
dd55c8
+
dd55c8
+done:
dd55c8
+	if (!op)
dd55c8
+		return false;
dd55c8
+
dd55c8
+	/* Just cancel since disconnect_cb will be cleaning up */
dd55c8
+	cancel_att_send_op(op);
dd55c8
+
dd55c8
+	return true;
dd55c8
+}
dd55c8
+
dd55c8
 bool bt_att_cancel(struct bt_att *att, unsigned int id)
dd55c8
 {
dd55c8
 	struct att_send_op *op;
dd55c8
@@ -1325,6 +1356,9 @@ bool bt_att_cancel(struct bt_att *att, unsigned int id)
dd55c8
 		return true;
dd55c8
 	}
dd55c8
 
dd55c8
+	if (att->in_disc)
dd55c8
+		return bt_att_disc_cancel(att, id);
dd55c8
+
dd55c8
 	op = queue_remove_if(att->req_queue, match_op_id, UINT_TO_PTR(id));
dd55c8
 	if (op)
dd55c8
 		goto done;
dd55c8
-- 
dd55c8
2.21.1
dd55c8