Commit 90fd131a authored by Florian Westphal's avatar Florian Westphal Committed by Pablo Neira Ayuso

netfilter: nf_tables: move dumper state allocation into ->start

Shaochun Chen points out we leak dumper filter state allocations
stored in dump_control->data in case there is an error before netlink sets
cb_running (after which ->done will be called at some point).

In order to fix this, add .start functions and do the allocations
there.

->done is going to clean up, and in case error occurs before
->start invocation no cleanups need to be done anymore.
Reported-by: default avatarshaochun chen <cscnull@gmail.com>
Signed-off-by: default avatarFlorian Westphal <fw@strlen.de>
Acked-by: default avatarDavid S. Miller <davem@davemloft.net>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 6613b617
...@@ -2271,6 +2271,39 @@ static int nf_tables_dump_rules(struct sk_buff *skb, ...@@ -2271,6 +2271,39 @@ static int nf_tables_dump_rules(struct sk_buff *skb,
return skb->len; return skb->len;
} }
static int nf_tables_dump_rules_start(struct netlink_callback *cb)
{
const struct nlattr * const *nla = cb->data;
struct nft_rule_dump_ctx *ctx = NULL;
if (nla[NFTA_RULE_TABLE] || nla[NFTA_RULE_CHAIN]) {
ctx = kzalloc(sizeof(*ctx), GFP_ATOMIC);
if (!ctx)
return -ENOMEM;
if (nla[NFTA_RULE_TABLE]) {
ctx->table = nla_strdup(nla[NFTA_RULE_TABLE],
GFP_ATOMIC);
if (!ctx->table) {
kfree(ctx);
return -ENOMEM;
}
}
if (nla[NFTA_RULE_CHAIN]) {
ctx->chain = nla_strdup(nla[NFTA_RULE_CHAIN],
GFP_ATOMIC);
if (!ctx->chain) {
kfree(ctx->table);
kfree(ctx);
return -ENOMEM;
}
}
}
cb->data = ctx;
return 0;
}
static int nf_tables_dump_rules_done(struct netlink_callback *cb) static int nf_tables_dump_rules_done(struct netlink_callback *cb)
{ {
struct nft_rule_dump_ctx *ctx = cb->data; struct nft_rule_dump_ctx *ctx = cb->data;
...@@ -2300,38 +2333,13 @@ static int nf_tables_getrule(struct net *net, struct sock *nlsk, ...@@ -2300,38 +2333,13 @@ static int nf_tables_getrule(struct net *net, struct sock *nlsk,
if (nlh->nlmsg_flags & NLM_F_DUMP) { if (nlh->nlmsg_flags & NLM_F_DUMP) {
struct netlink_dump_control c = { struct netlink_dump_control c = {
.start= nf_tables_dump_rules_start,
.dump = nf_tables_dump_rules, .dump = nf_tables_dump_rules,
.done = nf_tables_dump_rules_done, .done = nf_tables_dump_rules_done,
.module = THIS_MODULE, .module = THIS_MODULE,
.data = (void *)nla,
}; };
if (nla[NFTA_RULE_TABLE] || nla[NFTA_RULE_CHAIN]) {
struct nft_rule_dump_ctx *ctx;
ctx = kzalloc(sizeof(*ctx), GFP_ATOMIC);
if (!ctx)
return -ENOMEM;
if (nla[NFTA_RULE_TABLE]) {
ctx->table = nla_strdup(nla[NFTA_RULE_TABLE],
GFP_ATOMIC);
if (!ctx->table) {
kfree(ctx);
return -ENOMEM;
}
}
if (nla[NFTA_RULE_CHAIN]) {
ctx->chain = nla_strdup(nla[NFTA_RULE_CHAIN],
GFP_ATOMIC);
if (!ctx->chain) {
kfree(ctx->table);
kfree(ctx);
return -ENOMEM;
}
}
c.data = ctx;
}
return nft_netlink_dump_start_rcu(nlsk, skb, nlh, &c); return nft_netlink_dump_start_rcu(nlsk, skb, nlh, &c);
} }
...@@ -3181,6 +3189,18 @@ static int nf_tables_dump_sets(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -3181,6 +3189,18 @@ static int nf_tables_dump_sets(struct sk_buff *skb, struct netlink_callback *cb)
return skb->len; return skb->len;
} }
static int nf_tables_dump_sets_start(struct netlink_callback *cb)
{
struct nft_ctx *ctx_dump = NULL;
ctx_dump = kmemdup(cb->data, sizeof(*ctx_dump), GFP_ATOMIC);
if (ctx_dump == NULL)
return -ENOMEM;
cb->data = ctx_dump;
return 0;
}
static int nf_tables_dump_sets_done(struct netlink_callback *cb) static int nf_tables_dump_sets_done(struct netlink_callback *cb)
{ {
kfree(cb->data); kfree(cb->data);
...@@ -3208,18 +3228,12 @@ static int nf_tables_getset(struct net *net, struct sock *nlsk, ...@@ -3208,18 +3228,12 @@ static int nf_tables_getset(struct net *net, struct sock *nlsk,
if (nlh->nlmsg_flags & NLM_F_DUMP) { if (nlh->nlmsg_flags & NLM_F_DUMP) {
struct netlink_dump_control c = { struct netlink_dump_control c = {
.start = nf_tables_dump_sets_start,
.dump = nf_tables_dump_sets, .dump = nf_tables_dump_sets,
.done = nf_tables_dump_sets_done, .done = nf_tables_dump_sets_done,
.data = &ctx,
.module = THIS_MODULE, .module = THIS_MODULE,
}; };
struct nft_ctx *ctx_dump;
ctx_dump = kmalloc(sizeof(*ctx_dump), GFP_ATOMIC);
if (ctx_dump == NULL)
return -ENOMEM;
*ctx_dump = ctx;
c.data = ctx_dump;
return nft_netlink_dump_start_rcu(nlsk, skb, nlh, &c); return nft_netlink_dump_start_rcu(nlsk, skb, nlh, &c);
} }
...@@ -3869,6 +3883,15 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -3869,6 +3883,15 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
return -ENOSPC; return -ENOSPC;
} }
static int nf_tables_dump_set_start(struct netlink_callback *cb)
{
struct nft_set_dump_ctx *dump_ctx = cb->data;
cb->data = kmemdup(dump_ctx, sizeof(*dump_ctx), GFP_ATOMIC);
return cb->data ? 0 : -ENOMEM;
}
static int nf_tables_dump_set_done(struct netlink_callback *cb) static int nf_tables_dump_set_done(struct netlink_callback *cb)
{ {
kfree(cb->data); kfree(cb->data);
...@@ -4022,20 +4045,17 @@ static int nf_tables_getsetelem(struct net *net, struct sock *nlsk, ...@@ -4022,20 +4045,17 @@ static int nf_tables_getsetelem(struct net *net, struct sock *nlsk,
if (nlh->nlmsg_flags & NLM_F_DUMP) { if (nlh->nlmsg_flags & NLM_F_DUMP) {
struct netlink_dump_control c = { struct netlink_dump_control c = {
.start = nf_tables_dump_set_start,
.dump = nf_tables_dump_set, .dump = nf_tables_dump_set,
.done = nf_tables_dump_set_done, .done = nf_tables_dump_set_done,
.module = THIS_MODULE, .module = THIS_MODULE,
}; };
struct nft_set_dump_ctx *dump_ctx; struct nft_set_dump_ctx dump_ctx = {
.set = set,
dump_ctx = kmalloc(sizeof(*dump_ctx), GFP_ATOMIC); .ctx = ctx,
if (!dump_ctx) };
return -ENOMEM;
dump_ctx->set = set;
dump_ctx->ctx = ctx;
c.data = dump_ctx; c.data = &dump_ctx;
return nft_netlink_dump_start_rcu(nlsk, skb, nlh, &c); return nft_netlink_dump_start_rcu(nlsk, skb, nlh, &c);
} }
...@@ -4995,38 +5015,42 @@ static int nf_tables_dump_obj(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -4995,38 +5015,42 @@ static int nf_tables_dump_obj(struct sk_buff *skb, struct netlink_callback *cb)
return skb->len; return skb->len;
} }
static int nf_tables_dump_obj_done(struct netlink_callback *cb) static int nf_tables_dump_obj_start(struct netlink_callback *cb)
{ {
struct nft_obj_filter *filter = cb->data; const struct nlattr * const *nla = cb->data;
struct nft_obj_filter *filter = NULL;
if (filter) { if (nla[NFTA_OBJ_TABLE] || nla[NFTA_OBJ_TYPE]) {
kfree(filter->table); filter = kzalloc(sizeof(*filter), GFP_ATOMIC);
kfree(filter); if (!filter)
return -ENOMEM;
if (nla[NFTA_OBJ_TABLE]) {
filter->table = nla_strdup(nla[NFTA_OBJ_TABLE], GFP_ATOMIC);
if (!filter->table) {
kfree(filter);
return -ENOMEM;
}
}
if (nla[NFTA_OBJ_TYPE])
filter->type = ntohl(nla_get_be32(nla[NFTA_OBJ_TYPE]));
} }
cb->data = filter;
return 0; return 0;
} }
static struct nft_obj_filter * static int nf_tables_dump_obj_done(struct netlink_callback *cb)
nft_obj_filter_alloc(const struct nlattr * const nla[])
{ {
struct nft_obj_filter *filter; struct nft_obj_filter *filter = cb->data;
filter = kzalloc(sizeof(*filter), GFP_ATOMIC);
if (!filter)
return ERR_PTR(-ENOMEM);
if (nla[NFTA_OBJ_TABLE]) { if (filter) {
filter->table = nla_strdup(nla[NFTA_OBJ_TABLE], GFP_ATOMIC); kfree(filter->table);
if (!filter->table) { kfree(filter);
kfree(filter);
return ERR_PTR(-ENOMEM);
}
} }
if (nla[NFTA_OBJ_TYPE])
filter->type = ntohl(nla_get_be32(nla[NFTA_OBJ_TYPE]));
return filter; return 0;
} }
/* called with rcu_read_lock held */ /* called with rcu_read_lock held */
...@@ -5047,21 +5071,13 @@ static int nf_tables_getobj(struct net *net, struct sock *nlsk, ...@@ -5047,21 +5071,13 @@ static int nf_tables_getobj(struct net *net, struct sock *nlsk,
if (nlh->nlmsg_flags & NLM_F_DUMP) { if (nlh->nlmsg_flags & NLM_F_DUMP) {
struct netlink_dump_control c = { struct netlink_dump_control c = {
.start = nf_tables_dump_obj_start,
.dump = nf_tables_dump_obj, .dump = nf_tables_dump_obj,
.done = nf_tables_dump_obj_done, .done = nf_tables_dump_obj_done,
.module = THIS_MODULE, .module = THIS_MODULE,
.data = (void *)nla,
}; };
if (nla[NFTA_OBJ_TABLE] ||
nla[NFTA_OBJ_TYPE]) {
struct nft_obj_filter *filter;
filter = nft_obj_filter_alloc(nla);
if (IS_ERR(filter))
return -ENOMEM;
c.data = filter;
}
return nft_netlink_dump_start_rcu(nlsk, skb, nlh, &c); return nft_netlink_dump_start_rcu(nlsk, skb, nlh, &c);
} }
...@@ -5667,37 +5683,39 @@ static int nf_tables_dump_flowtable(struct sk_buff *skb, ...@@ -5667,37 +5683,39 @@ static int nf_tables_dump_flowtable(struct sk_buff *skb,
return skb->len; return skb->len;
} }
static int nf_tables_dump_flowtable_done(struct netlink_callback *cb) static int nf_tables_dump_flowtable_start(struct netlink_callback *cb)
{ {
struct nft_flowtable_filter *filter = cb->data; const struct nlattr * const *nla = cb->data;
struct nft_flowtable_filter *filter = NULL;
if (!filter) if (nla[NFTA_FLOWTABLE_TABLE]) {
return 0; filter = kzalloc(sizeof(*filter), GFP_ATOMIC);
if (!filter)
return -ENOMEM;
kfree(filter->table); filter->table = nla_strdup(nla[NFTA_FLOWTABLE_TABLE],
kfree(filter); GFP_ATOMIC);
if (!filter->table) {
kfree(filter);
return -ENOMEM;
}
}
cb->data = filter;
return 0; return 0;
} }
static struct nft_flowtable_filter * static int nf_tables_dump_flowtable_done(struct netlink_callback *cb)
nft_flowtable_filter_alloc(const struct nlattr * const nla[])
{ {
struct nft_flowtable_filter *filter; struct nft_flowtable_filter *filter = cb->data;
filter = kzalloc(sizeof(*filter), GFP_ATOMIC);
if (!filter) if (!filter)
return ERR_PTR(-ENOMEM); return 0;
if (nla[NFTA_FLOWTABLE_TABLE]) { kfree(filter->table);
filter->table = nla_strdup(nla[NFTA_FLOWTABLE_TABLE], kfree(filter);
GFP_ATOMIC);
if (!filter->table) { return 0;
kfree(filter);
return ERR_PTR(-ENOMEM);
}
}
return filter;
} }
/* called with rcu_read_lock held */ /* called with rcu_read_lock held */
...@@ -5717,20 +5735,13 @@ static int nf_tables_getflowtable(struct net *net, struct sock *nlsk, ...@@ -5717,20 +5735,13 @@ static int nf_tables_getflowtable(struct net *net, struct sock *nlsk,
if (nlh->nlmsg_flags & NLM_F_DUMP) { if (nlh->nlmsg_flags & NLM_F_DUMP) {
struct netlink_dump_control c = { struct netlink_dump_control c = {
.start = nf_tables_dump_flowtable_start,
.dump = nf_tables_dump_flowtable, .dump = nf_tables_dump_flowtable,
.done = nf_tables_dump_flowtable_done, .done = nf_tables_dump_flowtable_done,
.module = THIS_MODULE, .module = THIS_MODULE,
.data = (void *)nla,
}; };
if (nla[NFTA_FLOWTABLE_TABLE]) {
struct nft_flowtable_filter *filter;
filter = nft_flowtable_filter_alloc(nla);
if (IS_ERR(filter))
return -ENOMEM;
c.data = filter;
}
return nft_netlink_dump_start_rcu(nlsk, skb, nlh, &c); return nft_netlink_dump_start_rcu(nlsk, skb, nlh, &c);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment