soc: qcom: glink: Initialize local state while fetching ctx

Initialization of channel's local state is not done at the time of
fetching context from list of channels. This leads to race condition
if remote close happens during this time. Remote close will check if
local state is not open then delete channel from list. This leads to
use after free scenerio.

Initialize local state at the time of fetching channel context from
list of channels.

CRs-Fixed: 2155992
Change-Id: If113daba129191bd67ef2460eb4e87c2d5614403
Signed-off-by: Dhoat Harpal <hdhoat@codeaurora.org>
This commit is contained in:
Dhoat Harpal 2018-01-18 00:29:20 +05:30 committed by Gerrit - the friendly Code Review server
parent 1b75396524
commit bc0ac2b798

View file

@ -375,7 +375,7 @@ static void tx_func(struct kthread_work *work);
static struct channel_ctx *ch_name_to_ch_ctx_create(
struct glink_core_xprt_ctx *xprt_ctx,
const char *name);
const char *name, bool local);
static void ch_push_remote_rx_intent(struct channel_ctx *ctx, size_t size,
uint32_t riid, void *cookie);
@ -1870,13 +1870,14 @@ static void glink_ch_ctx_release(struct rwref_lock *ch_st_lock)
* it is not found and get reference of context.
* @xprt_ctx: Transport to search for a matching channel.
* @name: Name of the desired channel.
* @local: If called from local open or not
*
* Return: The channel corresponding to @name, NULL if a matching channel was
* not found AND a new channel could not be created.
*/
static struct channel_ctx *ch_name_to_ch_ctx_create(
struct glink_core_xprt_ctx *xprt_ctx,
const char *name)
const char *name, bool local)
{
struct channel_ctx *entry;
struct channel_ctx *ctx;
@ -1920,10 +1921,23 @@ check_ctx:
list_for_each_entry_safe(entry, temp, &xprt_ctx->channels,
port_list_node)
if (!strcmp(entry->name, name) && !entry->pending_delete) {
rwref_get(&entry->ch_state_lhb2);
/* port already exists */
if (entry->local_open_state != GLINK_CHANNEL_CLOSED
&& local) {
/* not ready to be re-opened */
GLINK_INFO_CH_XPRT(entry, xprt_ctx,
"%s: Ch not ready. State: %u\n",
__func__, entry->local_open_state);
rwref_put(&entry->ch_state_lhb2);
entry = NULL;
} else if (local) {
entry->local_open_state =
GLINK_CHANNEL_OPENING;
}
spin_unlock_irqrestore(&xprt_ctx->xprt_ctx_lock_lhb1,
flags);
kfree(ctx);
rwref_get(&entry->ch_state_lhb2);
rwref_write_put(&xprt_ctx->xprt_state_lhb0);
return entry;
}
@ -1954,6 +1968,8 @@ check_ctx:
ctx->transport_ptr = xprt_ctx;
rwref_get(&ctx->ch_state_lhb2);
if (local)
ctx->local_open_state = GLINK_CHANNEL_OPENING;
list_add_tail(&ctx->port_list_node, &xprt_ctx->channels);
GLINK_INFO_PERF_CH_XPRT(ctx, xprt_ctx,
@ -2639,23 +2655,13 @@ void *glink_open(const struct glink_open_config *cfg)
* look for an existing port structure which can occur in
* reopen and remote-open-first cases
*/
ctx = ch_name_to_ch_ctx_create(transport_ptr, cfg->name);
ctx = ch_name_to_ch_ctx_create(transport_ptr, cfg->name, true);
if (ctx == NULL) {
GLINK_ERR("%s:%s %s: Error - unable to allocate new channel\n",
cfg->transport, cfg->edge, __func__);
return ERR_PTR(-ENOMEM);
}
/* port already exists */
if (ctx->local_open_state != GLINK_CHANNEL_CLOSED) {
/* not ready to be re-opened */
GLINK_INFO_CH_XPRT(ctx, transport_ptr,
"%s: Channel not ready to be re-opened. State: %u\n",
__func__, ctx->local_open_state);
rwref_put(&ctx->ch_state_lhb2);
return ERR_PTR(-EBUSY);
}
/* initialize port structure */
ctx->user_priv = cfg->priv;
ctx->rx_intent_req_timeout_jiffies =
@ -2686,7 +2692,6 @@ void *glink_open(const struct glink_open_config *cfg)
ctx->local_xprt_req = best_id;
ctx->no_migrate = cfg->transport &&
!(cfg->options & GLINK_OPT_INITIAL_XPORT);
ctx->local_open_state = GLINK_CHANNEL_OPENING;
GLINK_INFO_PERF_CH(ctx,
"%s: local:GLINK_CHANNEL_CLOSED->GLINK_CHANNEL_OPENING\n",
__func__);
@ -4943,7 +4948,7 @@ static void glink_core_rx_cmd_ch_remote_open(struct glink_transport_if *if_ptr,
bool do_migrate;
glink_core_migration_edge_lock(if_ptr->glink_core_priv);
ctx = ch_name_to_ch_ctx_create(if_ptr->glink_core_priv, name);
ctx = ch_name_to_ch_ctx_create(if_ptr->glink_core_priv, name, false);
if (ctx == NULL) {
GLINK_ERR_XPRT(if_ptr->glink_core_priv,
"%s: invalid rcid %u received, name '%s'\n",
@ -5045,6 +5050,7 @@ static void glink_core_rx_cmd_ch_remote_close(
struct channel_ctx *ctx;
bool is_ch_fully_closed;
struct glink_core_xprt_ctx *xprt_ptr = if_ptr->glink_core_priv;
unsigned long flags;
ctx = xprt_rcid_to_ch_ctx_get(if_ptr->glink_core_priv, rcid);
if (!ctx) {
@ -5062,11 +5068,13 @@ static void glink_core_rx_cmd_ch_remote_close(
rwref_put(&ctx->ch_state_lhb2);
return;
}
spin_lock_irqsave(&ctx->transport_ptr->xprt_ctx_lock_lhb1, flags);
ctx->pending_delete = true;
spin_unlock_irqrestore(&ctx->transport_ptr->xprt_ctx_lock_lhb1, flags);
GLINK_INFO_CH(ctx, "%s: remote: OPENED->CLOSED\n", __func__);
is_ch_fully_closed = glink_core_remote_close_common(ctx, false);
ctx->pending_delete = true;
if_ptr->tx_cmd_ch_remote_close_ack(if_ptr, rcid);
if (is_ch_fully_closed) {