[nf-next PATCH v2 1/5] netfilter: nfnetlink_hook: Fix for EINTR due to index too large

Phil Sutter <[email protected]>
Newsgroups gmane.comp.security.firewalls.netfilter.devel
Message-ID <[email protected]>
When resuming a multipart dump and e->num_hook_entries has become lower
than the stored index, nfnl_hook_dump_one is not called and therefore
nl_dump_check_consistent will be passed an invalid nlmsghdr pointer.

Detect the situation and put an empty message to carry NLM_F_DUMP_INTR
flag.

This will help fix for concurrent NAT hook changes while dumping as well
since nfnl_hook_dump_nat will also abort before putting a single message
in that case.

The old code did assign NLM_F_DUMP_INTR to garbage in (or beyond) nlskb
with uninterrupted dumps, too: The mandatory last call saw 'i ==
e->num_hook_entries' and therefore incremented cb->seq. This patch makes
this a non-error (and the function will just return 0 without further
action).

Fixes: e2cf17d3774c ("netfilter: add new hook nfnl subsystem")
Signed-off-by: Phil Sutter <[email protected]>
---
 net/netfilter/nfnetlink_hook.c | 27 ++++++++++++++++++---------
 1 file changed, 18 insertions(+), 9 deletions(-)

diff --git a/net/netfilter/nfnetlink_hook.c b/net/netfilter/nfnetlink_hook.c
index 95005e9a6066..755d8f148db3 100644
--- a/net/netfilter/nfnetlink_hook.c
+++ b/net/netfilter/nfnetlink_hook.c
@@ -188,13 +188,21 @@ static int nfnl_hook_put_nft_ft_info(struct sk_buff *nlskb,
 	return 0;
 }
 
+static struct nlmsghdr *nfnl_put_get_hook_msg(struct sk_buff *nlskb,
+					      unsigned int seq, int family)
+{
+	u16 event = nfnl_msg_type(NFNL_SUBSYS_HOOK, NFNL_MSG_HOOK_GET);
+	unsigned int portid = NETLINK_CB(nlskb).portid;
+
+	return nfnl_msg_put(nlskb, portid, seq, event,
+			   NLM_F_MULTI, family, NFNETLINK_V0, 0);
+}
+
 static int nfnl_hook_dump_one(struct sk_buff *nlskb,
 			      const struct nfnl_dump_hook_data *ctx,
 			      const struct nf_hook_ops *ops, int priority,
 			      int family, unsigned int seq)
 {
-	u16 event = nfnl_msg_type(NFNL_SUBSYS_HOOK, NFNL_MSG_HOOK_GET);
-	unsigned int portid = NETLINK_CB(nlskb).portid;
 	struct nlmsghdr *nlh;
 	int ret = -EMSGSIZE;
 	u32 hooknum;
@@ -202,8 +210,7 @@ static int nfnl_hook_dump_one(struct sk_buff *nlskb,
 	char sym[KSYM_SYMBOL_LEN];
 	char *module_name;
 #endif
-	nlh = nfnl_msg_put(nlskb, portid, seq, event,
-			   NLM_F_MULTI, family, NFNETLINK_V0, 0);
+	nlh = nfnl_put_get_hook_msg(nlskb, seq, family);
 	if (!nlh)
 		goto nla_put_failure;
 
@@ -366,7 +373,7 @@ static int nfnl_hook_dump(struct sk_buff *nlskb,
 {
 	struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
 	struct nfnl_dump_hook_data *ctx = cb->data;
-	int err, family = nfmsg->nfgen_family;
+	int err = 0, family = nfmsg->nfgen_family;
 	struct net *net = sock_net(nlskb->sk);
 	struct nf_hook_ops * const *ops;
 	const struct nf_hook_entries *e;
@@ -378,14 +385,14 @@ static int nfnl_hook_dump(struct sk_buff *nlskb,
 	if (!e)
 		goto done;
 
-	if (IS_ERR(e)) {
+	if (IS_ERR(e) ||
+	    i > e->num_hook_entries ||
+	    (unsigned long)e != ctx->headv) {
 		cb->seq++;
+		err = -EINTR;
 		goto done;
 	}
 
-	if ((unsigned long)e != ctx->headv || i >= e->num_hook_entries)
-		cb->seq++;
-
 	ops = nf_hook_entries_get_hook_ops(e);
 
 	for (; i < e->num_hook_entries; i++) {
@@ -401,6 +408,8 @@ static int nfnl_hook_dump(struct sk_buff *nlskb,
 	}
 
 done:
+	if (err && !nlskb->len)
+		nfnl_put_get_hook_msg(nlskb, cb->nlh->nlmsg_seq, family);
 	nl_dump_check_consistent(cb, nlmsg_hdr(nlskb));
 	rcu_read_unlock();
 	cb->args[0] = i;
-- 
2.54.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.