Re: [PATCH nft 3/3] src: rework batching logic to fix possible use of uninitialized pages

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Jul 15, 2014 at 05:47:18PM +0200, Pablo Neira Ayuso wrote:
> This patch reworks the batching logic in several aspects:
> 
> 1) New batch pages are now always added into the batch page list in
>    first place. Then, in the send path, if the last batch page is
>    empty, it is removed from the batch list.
> 
> 2) nft_batch_page_add() is only called if the current batch page is
>    full. Therefore, it is guaranteed to find a valid netlink message
>    in the batch page when moving the tail that didn't fit into a new
>    batch page.
> 
> 3) The batch paging is initialized and released from the nft_netlink()
>    path.
> 
> 4) No more global struct mnl_nlmsg_batch *batch that points to the
>    current batch page. Instead, it is retrieved from the tail of the
>    batch list, which indicates the current batch page.
> 
> This patch fixes a crash due to access of uninitialized memory area in
> due to calling batch_page_add() with an empty batch in the send path,
> and the memleak of the batch page contents. Reported in:
> 
> http://patchwork.ozlabs.org/patch/367085/
> http://patchwork.ozlabs.org/patch/367774/
> 
> The patch is larger, but this save the zeroing of the batch page area.

Tested, it works find. Just a little question: is it better to release
all batches but the current after each message sending?

> 
> Reported-by: Yanchuan Nian <ycnian@xxxxxxxxx>
> Signed-off-by: Pablo Neira Ayuso <pablo@xxxxxxxxxxxxx>
> ---
>  src/main.c    |   14 +++++-----
>  src/mnl.c     |   82 ++++++++++++++++++++++++++++++++-------------------------
>  src/netlink.c |    1 -
>  3 files changed, 54 insertions(+), 43 deletions(-)
> 
> diff --git a/src/main.c b/src/main.c
> index 30ea2c6..552a35d 100644
> --- a/src/main.c
> +++ b/src/main.c
> @@ -173,6 +173,8 @@ static int nft_netlink(struct parser_state *state, struct list_head *msgs)
>  	bool batch_supported = netlink_batch_supported();
>  	int ret = 0;
>  
> +	mnl_batch_init();
> +
>  	batch_seqnum = mnl_batch_begin();
>  	list_for_each_entry(cmd, &state->cmds, list) {
>  		memset(&ctx, 0, sizeof(ctx));
> @@ -182,16 +184,14 @@ static int nft_netlink(struct parser_state *state, struct list_head *msgs)
>  		init_list_head(&ctx.list);
>  		ret = do_command(&ctx, cmd);
>  		if (ret < 0)
> -			return ret;
> +			goto out;
>  	}
>  	mnl_batch_end();
>  
> -	if (mnl_batch_ready())
> -		ret = netlink_batch_send(&err_list);
> -	else {
> -		mnl_batch_reset();
> +	if (!mnl_batch_ready())
>  		goto out;
> -	}
> +
> +	ret = netlink_batch_send(&err_list);
>  
>  	list_for_each_entry_safe(err, tmp, &err_list, head) {
>  		list_for_each_entry(cmd, &state->cmds, list) {
> @@ -208,6 +208,8 @@ static int nft_netlink(struct parser_state *state, struct list_head *msgs)
>  		}
>  	}
>  out:
> +	mnl_batch_reset();
> +
>  	list_for_each_entry_safe(cmd, next, &state->cmds, list) {
>  		list_del(&cmd->list);
>  		cmd_free(cmd);
> diff --git a/src/mnl.c b/src/mnl.c
> index e4f0339..42b11f5 100644
> --- a/src/mnl.c
> +++ b/src/mnl.c
> @@ -82,8 +82,6 @@ nft_mnl_talk(struct mnl_socket *nf_sock, const void *data, unsigned int len,
>   */
>  #define BATCH_PAGE_SIZE getpagesize() * 32
>  
> -static struct mnl_nlmsg_batch *batch;
> -
>  static struct mnl_nlmsg_batch *mnl_batch_alloc(void)
>  {
>  	static char *buf;
> @@ -93,11 +91,6 @@ static struct mnl_nlmsg_batch *mnl_batch_alloc(void)
>  	return mnl_nlmsg_batch_start(buf, BATCH_PAGE_SIZE);
>  }
>  
> -void mnl_batch_init(void)
> -{
> -	batch = mnl_batch_alloc();
> -}
> -
>  static LIST_HEAD(batch_page_list);
>  static int batch_num_pages;
>  
> @@ -106,35 +99,53 @@ struct batch_page {
>  	struct mnl_nlmsg_batch *batch;
>  };
>  
> +void mnl_batch_init(void)
> +{
> +	struct batch_page *batch_page;
> +
> +	batch_page = xmalloc(sizeof(struct batch_page));
> +	batch_page->batch = mnl_batch_alloc();
> +	batch_num_pages++;
> +	list_add_tail(&batch_page->head, &batch_page_list);
> +}
> +
> +static struct batch_page *nft_batch_page_current(void)
> +{
> +	return list_entry(batch_page_list.prev, struct batch_page, head);
> +}
> +
>  static void *nft_nlmsg_batch_current(void)
>  {
> -	return mnl_nlmsg_batch_current(batch);
> +	return mnl_nlmsg_batch_current(nft_batch_page_current()->batch);
>  }
>  
> -static void mnl_batch_page_add(void)
> +static void nft_batch_page_add(void)
>  {
> -	struct batch_page *batch_page;
>  	struct nlmsghdr *last_nlh;
>  
>  	/* Get the last message not fitting in the batch */
>  	last_nlh = nft_nlmsg_batch_current();
> -
> -	batch_page = xmalloc(sizeof(struct batch_page));
> -	batch_page->batch = batch;
> -	list_add_tail(&batch_page->head, &batch_page_list);
> -	batch_num_pages++;
> -	batch = mnl_batch_alloc();
> -
> +	/* Add new batch page */
> +	mnl_batch_init();
>  	/* Copy the last message not fitting to the new batch page */
>  	memcpy(nft_nlmsg_batch_current(), last_nlh, last_nlh->nlmsg_len);
>  	/* No overflow may happen as this is a new empty batch page */
> -	mnl_nlmsg_batch_next(batch);
> +	mnl_nlmsg_batch_next(nft_batch_page_current()->batch);
> +}
> +
> +static void nft_batch_page_release(struct batch_page *batch_page)
> +{
> +	list_del(&batch_page->head);
> +	xfree(mnl_nlmsg_batch_head(batch_page->batch));
> +	mnl_nlmsg_batch_stop(batch_page->batch);
> +	xfree(batch_page);
> +	batch_num_pages--;
>  }
>  
>  static void nft_batch_continue(void)
>  {
> -	if (!mnl_nlmsg_batch_next(batch))
> -		mnl_batch_page_add();
> +	if (!mnl_nlmsg_batch_next(nft_batch_page_current()->batch))
> +		nft_batch_page_add();
>  }
>  
>  static uint32_t mnl_batch_put(int type)
> @@ -171,12 +182,16 @@ bool mnl_batch_ready(void)
>  	/* Check if the batch only contains the initial and trailing batch
>  	 * messages. In that case, the batch is empty.
>  	 */
> -	return mnl_nlmsg_batch_size(batch) != (NLMSG_HDRLEN+sizeof(struct nfgenmsg)) * 2;
> +	return mnl_nlmsg_batch_size(nft_batch_page_current()->batch) !=
> +	       (NLMSG_HDRLEN+sizeof(struct nfgenmsg)) * 2;
>  }
>  
>  void mnl_batch_reset(void)
>  {
> -	mnl_nlmsg_batch_reset(batch);
> +	struct batch_page *batch_page, *next;
> +
> +	list_for_each_entry_safe(batch_page, next, &batch_page_list, head)
> +		nft_batch_page_release(batch_page);
>  }
>  
>  static void mnl_err_list_node_add(struct list_head *err_list, int error,
> @@ -226,12 +241,12 @@ static ssize_t mnl_nft_socket_sendmsg(const struct mnl_socket *nl)
>  		.msg_iov	= iov,
>  		.msg_iovlen	= batch_num_pages,
>  	};
> -	struct batch_page *batch_page, *next;
> +	struct batch_page *batch_page;
>  	int i = 0;
>  
>  	mnl_set_sndbuffer(nl);
>  
> -	list_for_each_entry_safe(batch_page, next, &batch_page_list, head) {
> +	list_for_each_entry(batch_page, &batch_page_list, head) {
>  		iov[i].iov_base = mnl_nlmsg_batch_head(batch_page->batch);
>  		iov[i].iov_len = mnl_nlmsg_batch_size(batch_page->batch);
>  		i++;
> @@ -243,10 +258,6 @@ static ssize_t mnl_nft_socket_sendmsg(const struct mnl_socket *nl)
>  					  sizeof(struct nfgenmsg));
>  		}
>  #endif
> -		list_del(&batch_page->head);
> -		xfree(batch_page->batch);
> -		xfree(batch_page);
> -		batch_num_pages--;
>  	}
>  
>  	return sendmsg(mnl_socket_get_fd(nl), &msg, 0);
> @@ -262,12 +273,13 @@ int mnl_batch_talk(struct mnl_socket *nl, struct list_head *err_list)
>  		.tv_usec	= 0
>  	};
>  
> -	if (!mnl_nlmsg_batch_is_empty(batch))
> -		mnl_batch_page_add();
> +	/* Remove last page from the batch if it's empty */
> +	if (mnl_nlmsg_batch_is_empty(nft_batch_page_current()->batch))
> +		nft_batch_page_release(nft_batch_page_current());
>  
>  	ret = mnl_nft_socket_sendmsg(nl);
>  	if (ret == -1)
> -		goto err;
> +		return -1;
>  
>  	FD_ZERO(&readfds);
>  	FD_SET(fd, &readfds);
> @@ -275,14 +287,14 @@ int mnl_batch_talk(struct mnl_socket *nl, struct list_head *err_list)
>  	/* receive and digest all the acknowledgments from the kernel. */
>  	ret = select(fd+1, &readfds, NULL, NULL, &tv);
>  	if (ret == -1)
> -		goto err;
> +		return -1;
>  
>  	while (ret > 0 && FD_ISSET(fd, &readfds)) {
>  		struct nlmsghdr *nlh = (struct nlmsghdr *)rcv_buf;
>  
>  		ret = mnl_socket_recvfrom(nl, rcv_buf, sizeof(rcv_buf));
>  		if (ret == -1)
> -			goto err;
> +			return -1;
>  
>  		ret = mnl_cb_run(rcv_buf, ret, 0, portid, NULL, NULL);
>  		/* Continue on error, make sure we get all acknowledgments */
> @@ -291,13 +303,11 @@ int mnl_batch_talk(struct mnl_socket *nl, struct list_head *err_list)
>  
>  		ret = select(fd+1, &readfds, NULL, NULL, &tv);
>  		if (ret == -1)
> -			goto err;
> +			return -1;
>  
>  		FD_ZERO(&readfds);
>  		FD_SET(fd, &readfds);
>  	}
> -err:
> -	mnl_nlmsg_batch_reset(batch);
>  	return ret;
>  }
>  
> diff --git a/src/netlink.c b/src/netlink.c
> index afad5a4..0176cb4 100644
> --- a/src/netlink.c
> +++ b/src/netlink.c
> @@ -59,7 +59,6 @@ static void __init netlink_open_sock(void)
>  {
>  	nf_sock = nfsock_open();
>  	fcntl(mnl_socket_get_fd(nf_sock), F_SETFL, O_NONBLOCK);
> -	mnl_batch_init();
>  }
>  
>  static void __exit netlink_close_sock(void)
> -- 
> 1.7.10.4
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Netfitler Users]     [LARTC]     [Bugtraq]     [Yosemite Forum]

  Powered by Linux