Procházet zdrojové kódy

cdp: Avoid deadlock for callback on diameter timeout

Callback function might do an operation which creates a new transaction,
which will result in a deadlock.

Fixes #3641
Morten Tryfoss před 1 rokem
rodič
revize
48f0e7fc9b
1 změnil soubory, kde provedl 31 přidání a 4 odebrání
  1. 31 4
      src/modules/cdp/transaction.c

+ 31 - 4
src/modules/cdp/transaction.c

@@ -206,6 +206,15 @@ void cdp_free_trans(cdp_trans_t *x)
 int cdp_trans_timer(time_t now, void *ptr)
 {
 	cdp_trans_t *x, *n;
+	cdp_trans_list_t *cb_queue = 0;
+	cb_queue = pkg_malloc(sizeof(cdp_trans_list_t));
+	if(!cb_queue) {
+		LOG_NO_MEM("pkg", sizeof(cdp_trans_list_t));
+		return 0;
+	}
+	cb_queue->head = 0;
+	cb_queue->tail = 0;
+
 	lock_get(trans_list->lock);
 	x = trans_list->head;
 	while(x) {
@@ -215,9 +224,6 @@ int cdp_trans_timer(time_t now, void *ptr)
 							.timeout); //Transaction has timed out waiting for response
 
 			x->ans = 0;
-			if(x->cb) {
-				(x->cb)(1, *(x->ptr), 0, (now - x->expires));
-			}
 			n = x->next;
 
 			if(x->prev)
@@ -228,7 +234,17 @@ int cdp_trans_timer(time_t now, void *ptr)
 				x->next->prev = x->prev;
 			else
 				trans_list->tail = x->prev;
-			if(x->auto_drop)
+
+			if(x->cb) {
+				/* queue cb to be done after list unlock */
+				x->next = 0;
+				x->prev = cb_queue->tail;
+				if(cb_queue->tail)
+					cb_queue->tail->next = x;
+				cb_queue->tail = x;
+				if(!cb_queue->head)
+					cb_queue->head = x;
+			} else if(x->auto_drop) /* if no callback, auto drop here */
 				cdp_free_trans(x);
 
 			x = n;
@@ -236,6 +252,17 @@ int cdp_trans_timer(time_t now, void *ptr)
 			x = x->next;
 	}
 	lock_release(trans_list->lock);
+
+	/* do all queued callbacks */
+	x = cb_queue->head;
+	while(x) {
+		(x->cb)(1, *(x->ptr), 0, (now - x->expires));
+		if(x->auto_drop)
+			cdp_free_trans(x);
+		x = x->next;
+	}
+	pkg_free(cb_queue);
+
 	return 1;
 }