If hooks are added or deleted, the nf_hook_entries buffer is reallocated and the RCU-protected pointer updated. If a dump requires multiple parts (exceeding sk_buff space) the second invocation may see a different nf_hook_entries pointer than the first one. Detect this just like the outer nfnl_hook_dump function does, by storing the pointer value in context. If it changes, signal user space it must restart. As with the stored loop counter, that stored pointer value must be manually zeroed upon successful loop completion since this inner function may run multiple times for different nf_hook_ops but with same context. Fixes: b010e2a4a9ac ("netfilter: nfnetlink_hook: Dump nat type chains") Signed-off-by: Phil Sutter --- net/netfilter/nfnetlink_hook.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/net/netfilter/nfnetlink_hook.c b/net/netfilter/nfnetlink_hook.c index 0657fbb3e605..e01e59eddd64 100644 --- a/net/netfilter/nfnetlink_hook.c +++ b/net/netfilter/nfnetlink_hook.c @@ -55,6 +55,7 @@ static int nf_netlink_dump_start_rcu(struct sock *nlsk, struct sk_buff *skb, struct nfnl_dump_hook_data { char devname[IFNAMSIZ]; unsigned long headv; + unsigned long natv; u8 hook; }; @@ -351,6 +352,15 @@ static int nfnl_hook_dump_nat(struct sk_buff *nlskb, if (!e) return 0; + if (!ctx->natv) + ctx->natv = (unsigned long)e; + + if (i >= e->num_hook_entries || + ctx->natv != (unsigned long)e) { + cb->seq++; + return -EINTR; + } + nat_ops = nf_hook_entries_get_hook_ops(e); for (; i < e->num_hook_entries; i++) { @@ -361,8 +371,10 @@ static int nfnl_hook_dump_nat(struct sk_buff *nlskb, break; } - if (!err) + if (!err) { + ctx->natv = 0 i = 0; + } cb->args[1] = i; return err; } -- 2.54.0