mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-31 02:37:10 +00:00 
			
		
		
		
	Merged revisions 219139 via svnmerge from
https://origsvn.digium.com/svn/asterisk/trunk ................ r219139 | mnicholson | 2009-09-17 10:18:01 -0500 (Thu, 17 Sep 2009) | 17 lines Merged revisions 219136 via svnmerge from https://origsvn.digium.com/svn/asterisk/branches/1.4 ........ r219136 | mnicholson | 2009-09-17 09:58:39 -0500 (Thu, 17 Sep 2009) | 10 lines Prevent a potential race condition and crash when hanging up a channel by removing the channel from the channel list before begining channel tear down. This fix may potentially cause problems with CDR backends that access the channel a CDR is associated with via the channel list. This fix makes the channel unavabile at the time when the CDR backend is invoked. This has been documented in include/asterisk/cdr.h. (closes issue #15316) Reported by: vmarrone Tested by: mnicholson Review: https://reviewboard.asterisk.org/r/362/ ........ ................ git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.6.1@219199 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
		| @@ -120,6 +120,12 @@ int ast_cdr_serialize_variables(struct ast_cdr *cdr, struct ast_str **buf, char | ||||
| void ast_cdr_free_vars(struct ast_cdr *cdr, int recur); | ||||
| int ast_cdr_copy_vars(struct ast_cdr *to_cdr, struct ast_cdr *from_cdr); | ||||
|  | ||||
| /*! | ||||
|  * \brief CDR backend callback | ||||
|  * \warning CDR backends should NOT attempt to access the channel associated | ||||
|  * with a CDR record.  This channel is not guaranteed to exist when the CDR | ||||
|  * backend is invoked. | ||||
|  */ | ||||
| typedef int (*ast_cdrbe)(struct ast_cdr *cdr); | ||||
|  | ||||
| /*! \brief Return TRUE if CDR subsystem is enabled */ | ||||
|   | ||||
| @@ -562,6 +562,8 @@ enum { | ||||
| 	 *  bridge terminates, this will allow the hangup in the pbx loop to be run instead. | ||||
| 	 *  */ | ||||
| 	AST_FLAG_BRIDGE_HANGUP_DONT = (1 << 18), | ||||
| 	/*! This flag indicates whether the channel is in the channel list or not. */ | ||||
| 	AST_FLAG_IN_CHANNEL_LIST = (1 << 19), | ||||
| }; | ||||
|  | ||||
| /*! \brief ast_bridge_config flags */ | ||||
|   | ||||
| @@ -930,6 +930,8 @@ alertpipe_failed: | ||||
|  | ||||
| 	tmp->tech = &null_tech; | ||||
|  | ||||
| 	ast_set_flag(tmp, AST_FLAG_IN_CHANNEL_LIST); | ||||
|  | ||||
| 	AST_RWLIST_WRLOCK(&channels); | ||||
| 	AST_RWLIST_INSERT_HEAD(&channels, tmp, chan_list); | ||||
| 	AST_RWLIST_UNLOCK(&channels); | ||||
| @@ -1351,17 +1353,21 @@ void ast_channel_free(struct ast_channel *chan) | ||||
| 	struct varshead *headp; | ||||
| 	struct ast_datastore *datastore = NULL; | ||||
| 	char name[AST_CHANNEL_NAME], *dashptr; | ||||
| 	int inlist; | ||||
| 	 | ||||
| 	headp=&chan->varshead; | ||||
| 	 | ||||
| 	AST_RWLIST_WRLOCK(&channels); | ||||
| 	if (!AST_RWLIST_REMOVE(&channels, chan, chan_list)) { | ||||
| 		ast_log(LOG_ERROR, "Unable to find channel in list to free. Assuming it has already been done.\n"); | ||||
| 	inlist = ast_test_flag(chan, AST_FLAG_IN_CHANNEL_LIST); | ||||
| 	if (inlist) { | ||||
| 		AST_RWLIST_WRLOCK(&channels); | ||||
| 		if (!AST_RWLIST_REMOVE(&channels, chan, chan_list)) { | ||||
| 			ast_debug(1, "Unable to find channel in list to free. Assuming it has already been done.\n"); | ||||
| 		} | ||||
| 		/* Lock and unlock the channel just to be sure nobody has it locked still | ||||
| 		   due to a reference retrieved from the channel list. */ | ||||
| 		ast_channel_lock(chan); | ||||
| 		ast_channel_unlock(chan); | ||||
| 	} | ||||
| 	/* Lock and unlock the channel just to be sure nobody has it locked still | ||||
| 	   due to a reference retrieved from the channel list. */ | ||||
| 	ast_channel_lock(chan); | ||||
| 	ast_channel_unlock(chan); | ||||
|  | ||||
| 	/* Get rid of each of the data stores on the channel */ | ||||
| 	while ((datastore = AST_LIST_REMOVE_HEAD(&chan->datastores, entry))) | ||||
| @@ -1440,7 +1446,8 @@ void ast_channel_free(struct ast_channel *chan) | ||||
|  | ||||
| 	ast_string_field_free_memory(chan); | ||||
| 	ast_free(chan); | ||||
| 	AST_RWLIST_UNLOCK(&channels); | ||||
| 	if (inlist) | ||||
| 		AST_RWLIST_UNLOCK(&channels); | ||||
|  | ||||
| 	/* Queue an unknown state, because, while we know that this particular | ||||
| 	 * instance is dead, we don't know the state of all other possible | ||||
| @@ -1663,6 +1670,16 @@ int ast_hangup(struct ast_channel *chan) | ||||
| 		ast_channel_unlock(chan); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	ast_channel_unlock(chan); | ||||
|  | ||||
| 	AST_RWLIST_WRLOCK(&channels); | ||||
| 	if (!AST_RWLIST_REMOVE(&channels, chan, chan_list)) { | ||||
| 		ast_log(LOG_ERROR, "Unable to find channel in list to free. Assuming it has already been done.\n"); | ||||
| 	} | ||||
| 	ast_clear_flag(chan, AST_FLAG_IN_CHANNEL_LIST); | ||||
| 	AST_RWLIST_UNLOCK(&channels); | ||||
|  | ||||
| 	ast_channel_lock(chan); | ||||
| 	free_translation(chan); | ||||
| 	/* Close audio stream */ | ||||
| 	if (chan->stream) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user