Skip to content

Commit

Permalink
tipc: fix race in disc create/delete
Browse files Browse the repository at this point in the history
Commit a21a584 (tipc: fix neighbor
detection problem after hw address change) introduces a race condition
involving tipc_disc_delete() and tipc_disc_add/remove_dest that can
cause TIPC to dereference the pointer to the bearer discovery request
structure after it has been freed since a stray pointer is left in the
bearer structure.

In order to fix the issue, the process of resetting the discovery
request handler is optimized: the discovery request handler and request
buffer are just reset instead of being freed, allocated and initialized.
As the request point is always valid and the request's lock is taken
while the request handler is reset, the race doesn't happen any more.

Reported-by: Erik Hugne <erik.hugne@ericsson.com>
Signed-off-by: Ying Xue <ying.xue@windriver.com>
Reviewed-by: Erik Hugne <erik.hugne@ericsson.com>
Tested-by: Erik Hugne <erik.hugne@ericsson.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
ying-xue authored and davem330 committed Apr 23, 2014
1 parent 28dd941 commit a8b9b96
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 20 deletions.
3 changes: 1 addition & 2 deletions net/tipc/bearer.c
Original file line number Diff line number Diff line change
Expand Up @@ -365,9 +365,8 @@ int tipc_enable_bearer(const char *name, u32 disc_domain, u32 priority)
static int tipc_reset_bearer(struct tipc_bearer *b_ptr)
{
pr_info("Resetting bearer <%s>\n", b_ptr->name);
tipc_disc_delete(b_ptr->link_req);
tipc_link_reset_list(b_ptr->identity);
tipc_disc_create(b_ptr, &b_ptr->bcast_addr);
tipc_disc_reset(b_ptr);
return 0;
}

Expand Down
53 changes: 35 additions & 18 deletions net/tipc/discover.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,22 +71,19 @@ struct tipc_link_req {
* @type: message type (request or response)
* @b_ptr: ptr to bearer issuing message
*/
static struct sk_buff *tipc_disc_init_msg(u32 type, struct tipc_bearer *b_ptr)
static void tipc_disc_init_msg(struct sk_buff *buf, u32 type,
struct tipc_bearer *b_ptr)
{
struct sk_buff *buf = tipc_buf_acquire(INT_H_SIZE);
struct tipc_msg *msg;
u32 dest_domain = b_ptr->domain;

if (buf) {
msg = buf_msg(buf);
tipc_msg_init(msg, LINK_CONFIG, type, INT_H_SIZE, dest_domain);
msg_set_non_seq(msg, 1);
msg_set_node_sig(msg, tipc_random);
msg_set_dest_domain(msg, dest_domain);
msg_set_bc_netid(msg, tipc_net_id);
b_ptr->media->addr2msg(&b_ptr->addr, msg_media_addr(msg));
}
return buf;
msg = buf_msg(buf);
tipc_msg_init(msg, LINK_CONFIG, type, INT_H_SIZE, dest_domain);
msg_set_non_seq(msg, 1);
msg_set_node_sig(msg, tipc_random);
msg_set_dest_domain(msg, dest_domain);
msg_set_bc_netid(msg, tipc_net_id);
b_ptr->media->addr2msg(&b_ptr->addr, msg_media_addr(msg));
}

/**
Expand Down Expand Up @@ -241,8 +238,9 @@ void tipc_disc_rcv(struct sk_buff *buf, struct tipc_bearer *b_ptr)
link_fully_up = link_working_working(link);

if ((type == DSC_REQ_MSG) && !link_fully_up) {
rbuf = tipc_disc_init_msg(DSC_RESP_MSG, b_ptr);
rbuf = tipc_buf_acquire(INT_H_SIZE);
if (rbuf) {
tipc_disc_init_msg(rbuf, DSC_RESP_MSG, b_ptr);
tipc_bearer_send(b_ptr->identity, rbuf, &media_addr);
kfree_skb(rbuf);
}
Expand Down Expand Up @@ -349,12 +347,11 @@ int tipc_disc_create(struct tipc_bearer *b_ptr, struct tipc_media_addr *dest)
if (!req)
return -ENOMEM;

req->buf = tipc_disc_init_msg(DSC_REQ_MSG, b_ptr);
if (!req->buf) {
kfree(req);
return -ENOMSG;
}
req->buf = tipc_buf_acquire(INT_H_SIZE);
if (!req->buf)
return -ENOMEM;

tipc_disc_init_msg(req->buf, DSC_REQ_MSG, b_ptr);
memcpy(&req->dest, dest, sizeof(*dest));
req->bearer_id = b_ptr->identity;
req->domain = b_ptr->domain;
Expand All @@ -379,3 +376,23 @@ void tipc_disc_delete(struct tipc_link_req *req)
kfree_skb(req->buf);
kfree(req);
}

/**
* tipc_disc_reset - reset object to send periodic link setup requests
* @b_ptr: ptr to bearer issuing requests
* @dest_domain: network domain to which links can be established
*/
void tipc_disc_reset(struct tipc_bearer *b_ptr)
{
struct tipc_link_req *req = b_ptr->link_req;

spin_lock_bh(&req->lock);
tipc_disc_init_msg(req->buf, DSC_REQ_MSG, b_ptr);
req->bearer_id = b_ptr->identity;
req->domain = b_ptr->domain;
req->num_nodes = 0;
req->timer_intv = TIPC_LINK_REQ_INIT;
k_start_timer(&req->timer, req->timer_intv);
tipc_bearer_send(req->bearer_id, req->buf, &req->dest);
spin_unlock_bh(&req->lock);
}
1 change: 1 addition & 0 deletions net/tipc/discover.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ struct tipc_link_req;

int tipc_disc_create(struct tipc_bearer *b_ptr, struct tipc_media_addr *dest);
void tipc_disc_delete(struct tipc_link_req *req);
void tipc_disc_reset(struct tipc_bearer *b_ptr);
void tipc_disc_add_dest(struct tipc_link_req *req);
void tipc_disc_remove_dest(struct tipc_link_req *req);
void tipc_disc_rcv(struct sk_buff *buf, struct tipc_bearer *b_ptr);
Expand Down

0 comments on commit a8b9b96

Please sign in to comment.