[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