[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]

 



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.

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