mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-31 18:55:19 +00:00 
			
		
		
		
	Lock the channel before calling the setoption callback
The channel needs to be locked before calling these callback functions. Also, sip_setoption needs to lock the pvt and a check p->rtp is non-null before using it. Review: https://reviewboard.asterisk.org/r/1220/ git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.8@324048 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
		| @@ -218,6 +218,7 @@ static void awesome_locking(struct local_pvt *p, struct ast_channel **outchan, s | ||||
| 	*outchan = p->chan; | ||||
| } | ||||
|  | ||||
| /* Called with ast locked */ | ||||
| static int local_setoption(struct ast_channel *ast, int option, void * data, int datalen) | ||||
| { | ||||
| 	int res = 0; | ||||
| @@ -226,27 +227,22 @@ static int local_setoption(struct ast_channel *ast, int option, void * data, int | ||||
| 	ast_chan_write_info_t *write_info; | ||||
|  | ||||
| 	if (option != AST_OPTION_CHANNEL_WRITE) { | ||||
| 		res = -1; | ||||
| 		goto setoption_cleanup; | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	write_info = data; | ||||
|  | ||||
| 	if (write_info->version != AST_CHAN_WRITE_INFO_T_VERSION) { | ||||
| 		ast_log(LOG_ERROR, "The chan_write_info_t type has changed, and this channel hasn't been updated!\n"); | ||||
| 		res = -1; | ||||
| 		goto setoption_cleanup; | ||||
| 		return -1 | ||||
| 	} | ||||
|  | ||||
| 	/* get the tech pvt */ | ||||
| 	ast_channel_lock(ast); | ||||
| 	if (!(p = ast->tech_pvt)) { | ||||
| 		ast_channel_unlock(ast); | ||||
| 		res = -1; | ||||
| 		goto setoption_cleanup; | ||||
| 		return -1; | ||||
| 	} | ||||
| 	ao2_ref(p, 1); | ||||
| 	ast_channel_unlock(ast); | ||||
| 	ast_channel_unlock(ast); /* Held when called, unlock before locking another channel */ | ||||
|  | ||||
| 	/* get the channel we are supposed to write to */ | ||||
| 	ao2_lock(p); | ||||
| @@ -273,6 +269,7 @@ setoption_cleanup: | ||||
| 	if (otherchan) { | ||||
| 		ast_channel_unref(otherchan); | ||||
| 	} | ||||
| 	ast_channel_lock(ast); /* Lock back before we leave */ | ||||
| 	return res; | ||||
| } | ||||
|  | ||||
| @@ -349,6 +346,7 @@ static struct ast_channel *local_bridgedchannel(struct ast_channel *chan, struct | ||||
| 	return bridged; | ||||
| } | ||||
|  | ||||
| /* Called with ast locked */ | ||||
| static int local_queryoption(struct ast_channel *ast, int option, void *data, int *datalen) | ||||
| { | ||||
| 	struct local_pvt *p; | ||||
| @@ -362,21 +360,18 @@ static int local_queryoption(struct ast_channel *ast, int option, void *data, in | ||||
| 	} | ||||
|  | ||||
| 	/* for some reason the channel is not locked in channel.c when this function is called */ | ||||
| 	ast_channel_lock(ast); | ||||
| 	if (!(p = ast->tech_pvt)) { | ||||
| 		ast_channel_unlock(ast); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	ao2_lock(p); | ||||
| 	if (!(tmp = IS_OUTBOUND(ast, p) ? p->owner : p->chan)) { | ||||
| 		ao2_unlock(p); | ||||
| 		ast_channel_unlock(ast); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	ast_channel_ref(tmp); | ||||
| 	ao2_unlock(p); | ||||
| 	ast_channel_unlock(ast); | ||||
| 	ast_channel_unlock(ast); /* Held when called, unlock before locking another channel */ | ||||
|  | ||||
| 	ast_channel_lock(tmp); | ||||
| 	if (!(bridged = ast_bridged_channel(tmp))) { | ||||
| @@ -395,6 +390,7 @@ query_cleanup: | ||||
| 	if (tmp) { | ||||
| 		tmp = ast_channel_unref(tmp); | ||||
| 	} | ||||
| 	ast_channel_lock(ast); /* Lock back before we leave */ | ||||
|  | ||||
| 	return res; | ||||
| } | ||||
|   | ||||
| @@ -4226,15 +4226,23 @@ static int sip_setoption(struct ast_channel *chan, int option, void *data, int d | ||||
| 	int res = -1; | ||||
| 	struct sip_pvt *p = chan->tech_pvt; | ||||
| 
 | ||||
| 	sip_pvt_lock(p); | ||||
| 
 | ||||
| 	switch (option) { | ||||
| 	case AST_OPTION_FORMAT_READ: | ||||
| 		res = ast_rtp_instance_set_read_format(p->rtp, *(int *) data); | ||||
| 		if (p->rtp) { | ||||
| 			res = ast_rtp_instance_set_read_format(p->rtp, *(int *) data); | ||||
| 		} | ||||
| 		break; | ||||
| 	case AST_OPTION_FORMAT_WRITE: | ||||
| 		res = ast_rtp_instance_set_write_format(p->rtp, *(int *) data); | ||||
| 		if (p->rtp) { | ||||
| 			res = ast_rtp_instance_set_write_format(p->rtp, *(int *) data); | ||||
| 		} | ||||
| 		break; | ||||
| 	case AST_OPTION_MAKE_COMPATIBLE: | ||||
| 		res = ast_rtp_instance_make_compatible(chan, p->rtp, (struct ast_channel *) data); | ||||
| 		if (p->rtp) { | ||||
| 			res = ast_rtp_instance_make_compatible(chan, p->rtp, (struct ast_channel *) data); | ||||
| 		} | ||||
| 		break; | ||||
| 	case AST_OPTION_DIGIT_DETECT: | ||||
| 		if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) || | ||||
| @@ -4262,6 +4270,8 @@ static int sip_setoption(struct ast_channel *chan, int option, void *data, int d | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	sip_pvt_unlock(p); | ||||
| 
 | ||||
| 	return res; | ||||
| } | ||||
| 
 | ||||
| @@ -4273,16 +4283,16 @@ static int sip_queryoption(struct ast_channel *chan, int option, void *data, int | ||||
| 	struct sip_pvt *p = (struct sip_pvt *) chan->tech_pvt; | ||||
| 	char *cp; | ||||
| 
 | ||||
| 	sip_pvt_lock(p); | ||||
| 
 | ||||
| 	switch (option) { | ||||
| 	case AST_OPTION_T38_STATE: | ||||
| 		/* Make sure we got an ast_t38_state enum passed in */ | ||||
| 		if (*datalen != sizeof(enum ast_t38_state)) { | ||||
| 			ast_log(LOG_ERROR, "Invalid datalen for AST_OPTION_T38_STATE option. Expected %d, got %d\n", (int)sizeof(enum ast_t38_state), *datalen); | ||||
| 			return -1; | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		sip_pvt_lock(p); | ||||
| 
 | ||||
| 		/* Now if T38 support is enabled we need to look and see what the current state is to get what we want to report back */ | ||||
| 		if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT)) { | ||||
| 			switch (p->t38.state) { | ||||
| @@ -4298,8 +4308,6 @@ static int sip_queryoption(struct ast_channel *chan, int option, void *data, int | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		sip_pvt_unlock(p); | ||||
| 
 | ||||
| 		*((enum ast_t38_state *) data) = state; | ||||
| 		res = 0; | ||||
| 
 | ||||
| @@ -4331,6 +4339,8 @@ static int sip_queryoption(struct ast_channel *chan, int option, void *data, int | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	sip_pvt_unlock(p); | ||||
| 
 | ||||
| 	return res; | ||||
| } | ||||
| 
 | ||||
|   | ||||
| @@ -572,10 +572,10 @@ struct ast_channel_tech { | ||||
| 	/*! \brief Fix up a channel:  If a channel is consumed, this is called.  Basically update any ->owner links */ | ||||
| 	int (* const fixup)(struct ast_channel *oldchan, struct ast_channel *newchan); | ||||
|  | ||||
| 	/*! \brief Set a given option */ | ||||
| 	/*! \brief Set a given option. Called with chan locked */ | ||||
| 	int (* const setoption)(struct ast_channel *chan, int option, void *data, int datalen); | ||||
|  | ||||
| 	/*! \brief Query a given option */ | ||||
| 	/*! \brief Query a given option. Called with chan locked */ | ||||
| 	int (* const queryoption)(struct ast_channel *chan, int option, void *data, int *datalen); | ||||
|  | ||||
| 	/*! \brief Blind transfer other side (see app_transfer.c and ast_transfer() */ | ||||
|   | ||||
| @@ -7387,28 +7387,42 @@ enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_cha | ||||
| /*! \brief Sets an option on a channel */ | ||||
| int ast_channel_setoption(struct ast_channel *chan, int option, void *data, int datalen, int block) | ||||
| { | ||||
| 	int res; | ||||
|  | ||||
| 	ast_channel_lock(chan); | ||||
| 	if (!chan->tech->setoption) { | ||||
| 		errno = ENOSYS; | ||||
| 		ast_channel_unlock(chan); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	if (block) | ||||
| 		ast_log(LOG_ERROR, "XXX Blocking not implemented yet XXX\n"); | ||||
|  | ||||
| 	return chan->tech->setoption(chan, option, data, datalen); | ||||
| 	res = chan->tech->setoption(chan, option, data, datalen); | ||||
| 	ast_channel_unlock(chan); | ||||
|  | ||||
| 	return res; | ||||
| } | ||||
|  | ||||
| int ast_channel_queryoption(struct ast_channel *chan, int option, void *data, int *datalen, int block) | ||||
| { | ||||
| 	int res; | ||||
|  | ||||
| 	ast_channel_lock(chan); | ||||
| 	if (!chan->tech->queryoption) { | ||||
| 		errno = ENOSYS; | ||||
| 		ast_channel_unlock(chan); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	if (block) | ||||
| 		ast_log(LOG_ERROR, "XXX Blocking not implemented yet XXX\n"); | ||||
|  | ||||
| 	return chan->tech->queryoption(chan, option, data, datalen); | ||||
| 	res = chan->tech->queryoption(chan, option, data, datalen); | ||||
| 	ast_channel_unlock(chan); | ||||
|  | ||||
| 	return res; | ||||
| } | ||||
|  | ||||
| struct tonepair_def { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user