Review Board 1.7.16


IAX2: fix race condition when transferrring.

Review Request #2594 - Created June 4, 2013 and submitted

Alec Davis
1.8 to trunk
ASTERISK-21409
Reviewers
asterisk-dev
Asterisk
1). When touching the bridgecallno, we need to lock it.

2). stop_stuff() which calls iax2_destroy_helper()
    Assumes the lock on the pvt is already held, when iax2_destroy_helper() is called.
    Thus we need to lock the bridgecallno pvt before we call stop_stuff(iaxs[fr->callno]->bridgecallno);

3). When evaluating the state of 'callno->transferring' of the current leg, we can't change it to READY unless the bridgecallno is locked.
    Why, if we are interrupted by the other call leg before 'transferring = TRANSFER_RELEASED', the interrupt will find that it is READY and that the bridgecallno is also READY so Releases the legs.
    Then the first call leg in this example, finishes execution, and Releases the legs AGAIN!!!!!!!
    Interleaving thread execution gets interesting as well - see timeline below from June 5. 

Debug captures when it went wrong.
[May 31 14:44:01] VERBOSE[30820] chan_iax2.c:     -- Channel 'IAX2/auckland-13262' ready to transfer
[May 31 14:44:01] VERBOSE[30824][C-00000536] chan_iax2.c:     -- Channel 'IAX2/auckland-20457' ready to transfer
[May 31 14:44:01] VERBOSE[30824][C-00000536] chan_iax2.c:     -- Releasing IAX2/auckland-20457 and IAX2/auckland-13262
[May 31 14:44:01] VERBOSE[30820] chan_iax2.c:     -- Releasing IAX2/auckland-13262 and IAX2/auckland-20457
[May 31 14:44:01] DEBUG[30824][C-00000536] sched.c: Attempted to delete nonexistent schedule entry 209951!
[May 31 14:44:01] ERROR[30824][C-00000536] lock.c: chan_iax2.c line 1918 (iax2_destroy_helper): mutex '&iaxsl[pvt->callno]' freed more times than we've locked!
[May 31 14:44:01] ERROR[30824][C-00000536] lock.c: chan_iax2.c line 1918 (iax2_destroy_helper): Error releasing mutex: Operation not permitted

[Jun  5 19:53:43] VERBOSE[25606][C-00000000] chan_iax2.c:     -- Channel 'IAX2/auckland-19065' ready to transfer
[Jun  5 19:53:43] VERBOSE[25604][C-00000000] chan_iax2.c:     -- Channel 'IAX2/auckland-20047' ready to transfer
[Jun  5 19:53:43] VERBOSE[25606][C-00000000] chan_iax2.c:     -- Releasing IAX2/auckland-19065 and IAX2/auckland-20047
[Jun  5 19:53:43] VERBOSE[25604][C-00000000] chan_iax2.c:     -- Releasing IAX2/auckland-20047 and IAX2/auckland-19065
[Jun  5 19:53:43] VERBOSE[25606][C-00000000] chan_iax2.c:     -- Channel 'IAX2/auckland-19065' finished transfer
[Jun  5 19:53:43] DEBUG[25604][C-00000000] sched.c: Attempted to delete nonexistent schedule entry 17!
[Jun  5 19:53:43] VERBOSE[25604][C-00000000] chan_iax2.c:     -- Channel 'IAX2/auckland-20047' finished transfer


A time line of 2 threads interleaving that shows how the Jun 5 capture may have come about.
The execution path seems to switch threads as we print/log data.
thread 25606                thread 25604
======================      =======================
if US == BEGIN
   US = READY
   "ready to transfer"
                            if US == BEGIN
                               US = READY
                               "ready to transfer"
    if THEY == READY
       "Releasing .."
                               if THEY == READY
                                  "Releasing .."
       THEY = RELEASED
       US = RELEASED
       stopstuff(US)
       stopstuff(THEM)
       "finished transfer"

                                   THEY = RELEASED
                                   US = RELEASED
                                   stopstuff(US)
                                   stopstuff(THEM) (sched.c: Attempted to delete nonexistent schedule entry 17!)
                                   "finished transfer"

Yes. Between sites. many test calls and real calls being transferred back down the same trunk

Diff revision 3

This is not the most recent revision of the diff. The latest diff is revision 4. See what's changed.

1 2 3 4
1 2 3 4

  1. branches/11/channels/chan_iax2.c: Loading...
branches/11/channels/chan_iax2.c
Revision 387927 New Change
[20] 11506 lines
[+20] [+] immediatedial:
11507
					iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_FACILITY_NOT_SUBSCRIBED);
11507
					iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_FACILITY_NOT_SUBSCRIBED);
11508
					send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
11508
					send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1);
11509
				}
11509
				}
11510
				break;
11510
				break;
11511
			case IAX_COMMAND_TXREJ:
11511
			case IAX_COMMAND_TXREJ:
11512
				iaxs[fr->callno]->transferring = 0;
11512
				iaxs[fr->callno]->transferring = TRANSFER_NONE;
11513
				ast_verb(3, "Channel '%s' unable to transfer\n", iaxs[fr->callno]->owner ? ast_channel_name(iaxs[fr->callno]->owner) : "<Unknown>");
11513
				ast_verb(3, "Channel '%s' unable to transfer\n", iaxs[fr->callno]->owner ? ast_channel_name(iaxs[fr->callno]->owner) : "<Unknown>");
11514
				memset(&iaxs[fr->callno]->transfer, 0, sizeof(iaxs[fr->callno]->transfer));
11514
				memset(&iaxs[fr->callno]->transfer, 0, sizeof(iaxs[fr->callno]->transfer));
11515
				if (iaxs[fr->callno]->bridgecallno) {
11515
				if (iaxs[fr->callno]->bridgecallno) {
11516
					if (iaxs[iaxs[fr->callno]->bridgecallno]->transferring) {
11516
					if (iaxs[iaxs[fr->callno]->bridgecallno]->transferring) {
11517
						iaxs[iaxs[fr->callno]->bridgecallno]->transferring = 0;
11517
						iaxs[iaxs[fr->callno]->bridgecallno]->transferring = TRANSFER_NONE;
11518
						send_command(iaxs[iaxs[fr->callno]->bridgecallno], AST_FRAME_IAX, IAX_COMMAND_TXREJ, 0, NULL, 0, -1);
11518
						send_command(iaxs[iaxs[fr->callno]->bridgecallno], AST_FRAME_IAX, IAX_COMMAND_TXREJ, 0, NULL, 0, -1);
11519
					}
11519
					}
11520
				}
11520
				}
11521
				break;
11521
				break;
11522
			case IAX_COMMAND_TXREADY:
11522
			case IAX_COMMAND_TXREADY:
11523
				if ((iaxs[fr->callno]->transferring == TRANSFER_BEGIN) ||
11523
				if (iaxs[fr->callno]->bridgecallno) {
11524
				    (iaxs[fr->callno]->transferring == TRANSFER_MBEGIN)) {
11524
					while (ast_mutex_trylock(&iaxsl[iaxs[fr->callno]->bridgecallno])) {
11525
					if (iaxs[fr->callno]->transferring == TRANSFER_MBEGIN)
11525
						DEADLOCK_AVOIDANCE(&iaxsl[fr->callno]);

    
   
11526
					}

    
   
11527
					if (!iaxs[fr->callno]) {

    
   
11528
						break;

    
   
11529
					}

    
   
11530
				}

    
   
11531

   

    
   
11532
				if (iaxs[fr->callno]->transferring == TRANSFER_MBEGIN) {
11526
						iaxs[fr->callno]->transferring = TRANSFER_MREADY;
11533
					iaxs[fr->callno]->transferring = TRANSFER_MREADY;
11527
					else
11534
				} else if (iaxs[fr->callno]->transferring == TRANSFER_BEGIN) {
11528
						iaxs[fr->callno]->transferring = TRANSFER_READY;
11535
					iaxs[fr->callno]->transferring = TRANSFER_READY;

    
   
11536
				} else {

    
   
11537
					if (iaxs[fr->callno]->bridgecallno) {

    
   
11538
						ast_mutex_unlock(&iaxsl[iaxs[fr->callno]->bridgecallno]);

    
   
11539
					}

    
   
11540
					break;

    
   
11541
				}

    
   
11542

   
11529
					ast_verb(3, "Channel '%s' ready to transfer\n", iaxs[fr->callno]->owner ? ast_channel_name(iaxs[fr->callno]->owner) : "<Unknown>");
11543
				ast_verb(3, "Channel '%s' ready to transfer\n", iaxs[fr->callno]->owner ? ast_channel_name(iaxs[fr->callno]->owner) : "<Unknown>");

    
   
11544

   
11530
					if (iaxs[fr->callno]->bridgecallno) {
11545
				if (iaxs[fr->callno]->bridgecallno) {
11531
						if ((iaxs[iaxs[fr->callno]->bridgecallno]->transferring == TRANSFER_READY) ||
11546
					if (iaxs[iaxs[fr->callno]->bridgecallno]->transferring == TRANSFER_MREADY) {
11532
						    (iaxs[iaxs[fr->callno]->bridgecallno]->transferring == TRANSFER_MREADY)) {

   
11533
							/* They're both ready, now release them. */
11547
						/* They're both ready, now release them. */
11534
							if (iaxs[fr->callno]->transferring == TRANSFER_MREADY) {

   
11535
								ast_verb(3, "Attempting media bridge of %s and %s\n", iaxs[fr->callno]->owner ? ast_channel_name(iaxs[fr->callno]->owner) : "<Unknown>",
11548
						ast_verb(3, "Attempting media bridge of %s and %s\n", iaxs[fr->callno]->owner ? ast_channel_name(iaxs[fr->callno]->owner) : "<Unknown>",
11536
										iaxs[iaxs[fr->callno]->bridgecallno]->owner ? ast_channel_name(iaxs[iaxs[fr->callno]->bridgecallno]->owner) : "<Unknown>");
11549
							iaxs[iaxs[fr->callno]->bridgecallno]->owner ? ast_channel_name(iaxs[iaxs[fr->callno]->bridgecallno]->owner) : "<Unknown>");
11537

    
   
11550

   
11538
								iaxs[iaxs[fr->callno]->bridgecallno]->transferring = TRANSFER_MEDIA;
11551
						iaxs[iaxs[fr->callno]->bridgecallno]->transferring = TRANSFER_MEDIA;
11539
								iaxs[fr->callno]->transferring = TRANSFER_MEDIA;
11552
						iaxs[fr->callno]->transferring = TRANSFER_MEDIA;
11540

    
   
11553

   
11541
								memset(&ied0, 0, sizeof(ied0));
11554
						memset(&ied0, 0, sizeof(ied0));
11542
								memset(&ied1, 0, sizeof(ied1));
11555
						memset(&ied1, 0, sizeof(ied1));
11543
								iax_ie_append_short(&ied0, IAX_IE_CALLNO, iaxs[iaxs[fr->callno]->bridgecallno]->peercallno);
11556
						iax_ie_append_short(&ied0, IAX_IE_CALLNO, iaxs[iaxs[fr->callno]->bridgecallno]->peercallno);
11544
								iax_ie_append_short(&ied1, IAX_IE_CALLNO, iaxs[fr->callno]->peercallno);
11557
						iax_ie_append_short(&ied1, IAX_IE_CALLNO, iaxs[fr->callno]->peercallno);
11545
								send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_TXMEDIA, 0, ied0.buf, ied0.pos, -1);
11558
						send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_TXMEDIA, 0, ied0.buf, ied0.pos, -1);
11546
								send_command(iaxs[iaxs[fr->callno]->bridgecallno], AST_FRAME_IAX, IAX_COMMAND_TXMEDIA, 0, ied1.buf, ied1.pos, -1);
11559
						send_command(iaxs[iaxs[fr->callno]->bridgecallno], AST_FRAME_IAX, IAX_COMMAND_TXMEDIA, 0, ied1.buf, ied1.pos, -1);
11547
							} else {
11560

   

    
   
11561
					} else if (iaxs[iaxs[fr->callno]->bridgecallno]->transferring == TRANSFER_READY) {

    
   
11562
						/* They're both ready, now release them. */
11548
								ast_verb(3, "Releasing %s and %s\n", iaxs[fr->callno]->owner ? ast_channel_name(iaxs[fr->callno]->owner) : "<Unknown>",
11563
						ast_verb(3, "Releasing %s and %s\n", iaxs[fr->callno]->owner ? ast_channel_name(iaxs[fr->callno]->owner) : "<Unknown>",
11549
										iaxs[iaxs[fr->callno]->bridgecallno]->owner ? ast_channel_name(iaxs[iaxs[fr->callno]->bridgecallno]->owner) : "<Unknown>");
11564
							iaxs[iaxs[fr->callno]->bridgecallno]->owner ? ast_channel_name(iaxs[iaxs[fr->callno]->bridgecallno]->owner) : "<Unknown>");
11550

    
   
11565

   
11551
								iaxs[iaxs[fr->callno]->bridgecallno]->transferring = TRANSFER_RELEASED;
11566
						iaxs[iaxs[fr->callno]->bridgecallno]->transferring = TRANSFER_RELEASED;
11552
								iaxs[fr->callno]->transferring = TRANSFER_RELEASED;
11567
						iaxs[fr->callno]->transferring = TRANSFER_RELEASED;
[+20] [20] 8 lines
[+20] immediatedial:
11561
								memset(&ied1, 0, sizeof(ied1));
11576
						memset(&ied1, 0, sizeof(ied1));
11562
								iax_ie_append_short(&ied0, IAX_IE_CALLNO, iaxs[iaxs[fr->callno]->bridgecallno]->peercallno);
11577
						iax_ie_append_short(&ied0, IAX_IE_CALLNO, iaxs[iaxs[fr->callno]->bridgecallno]->peercallno);
11563
								iax_ie_append_short(&ied1, IAX_IE_CALLNO, iaxs[fr->callno]->peercallno);
11578
						iax_ie_append_short(&ied1, IAX_IE_CALLNO, iaxs[fr->callno]->peercallno);
11564
								send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_TXREL, 0, ied0.buf, ied0.pos, -1);
11579
						send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_TXREL, 0, ied0.buf, ied0.pos, -1);
11565
								send_command(iaxs[iaxs[fr->callno]->bridgecallno], AST_FRAME_IAX, IAX_COMMAND_TXREL, 0, ied1.buf, ied1.pos, -1);
11580
						send_command(iaxs[iaxs[fr->callno]->bridgecallno], AST_FRAME_IAX, IAX_COMMAND_TXREL, 0, ied1.buf, ied1.pos, -1);
11566
							}

   
11567

    
   
11581

   
11568
						}
11582
					}

    
   
11583
					ast_mutex_unlock(&iaxsl[iaxs[fr->callno]->bridgecallno]);
11569
					}
11584
				}
11570
				}
11585
				ast_verb(3, "Channel '%s' finished transfer\n", iaxs[fr->callno]->owner ? ast_channel_name(iaxs[fr->callno]->owner) : "<Unknown>");
11571
				break;
11586
				break;
11572
			case IAX_COMMAND_TXREQ:
11587
			case IAX_COMMAND_TXREQ:
11573
				try_transfer(iaxs[fr->callno], &ies);
11588
				try_transfer(iaxs[fr->callno], &ies);
11574
				break;
11589
				break;
11575
			case IAX_COMMAND_TXCNT:
11590
			case IAX_COMMAND_TXCNT:
[+20] [20] 3489 lines
  1. branches/11/channels/chan_iax2.c: Loading...

https://reviewboard.asterisk.org/ runs on a server provided by Digium, Inc. and uses bandwidth donated to the open source Asterisk community by API Digital Communications in Huntsville, AL USA.
Please report problems with this site to asteriskteam@digium.com.