Commit a95bc734 authored by Johannes Berg's avatar Johannes Berg Committed by David S. Miller

netlink: fix policy dump leak

If userspace doesn't complete the policy dump, we leak the
allocated state. Fix this.

Fixes: d07dcf9a ("netlink: add infrastructure to expose policies to userspace")
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Reviewed-by: default avatarJakub Kicinski <kuba@kernel.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ef9da46d
......@@ -1934,7 +1934,8 @@ void nla_get_range_signed(const struct nla_policy *pt,
int netlink_policy_dump_start(const struct nla_policy *policy,
unsigned int maxtype,
unsigned long *state);
bool netlink_policy_dump_loop(unsigned long *state);
bool netlink_policy_dump_loop(unsigned long state);
int netlink_policy_dump_write(struct sk_buff *skb, unsigned long state);
void netlink_policy_dump_free(unsigned long state);
#endif
......@@ -1079,7 +1079,7 @@ static int ctrl_dumppolicy(struct sk_buff *skb, struct netlink_callback *cb)
if (err)
return err;
while (netlink_policy_dump_loop(&cb->args[1])) {
while (netlink_policy_dump_loop(cb->args[1])) {
void *hdr;
struct nlattr *nest;
......@@ -1113,6 +1113,12 @@ static int ctrl_dumppolicy(struct sk_buff *skb, struct netlink_callback *cb)
return skb->len;
}
static int ctrl_dumppolicy_done(struct netlink_callback *cb)
{
netlink_policy_dump_free(cb->args[1]);
return 0;
}
static const struct genl_ops genl_ctrl_ops[] = {
{
.cmd = CTRL_CMD_GETFAMILY,
......@@ -1123,6 +1129,7 @@ static const struct genl_ops genl_ctrl_ops[] = {
{
.cmd = CTRL_CMD_GETPOLICY,
.dumpit = ctrl_dumppolicy,
.done = ctrl_dumppolicy_done,
},
};
......
......@@ -84,7 +84,6 @@ int netlink_policy_dump_start(const struct nla_policy *policy,
unsigned int policy_idx;
int err;
/* also returns 0 if "*_state" is our ERR_PTR() end marker */
if (*_state)
return 0;
......@@ -140,21 +139,11 @@ static bool netlink_policy_dump_finished(struct nl_policy_dump *state)
!state->policies[state->policy_idx].policy;
}
bool netlink_policy_dump_loop(unsigned long *_state)
bool netlink_policy_dump_loop(unsigned long _state)
{
struct nl_policy_dump *state = (void *)*_state;
if (IS_ERR(state))
return false;
if (netlink_policy_dump_finished(state)) {
kfree(state);
/* store end marker instead of freed state */
*_state = (unsigned long)ERR_PTR(-ENOENT);
return false;
}
struct nl_policy_dump *state = (void *)_state;
return true;
return !netlink_policy_dump_finished(state);
}
int netlink_policy_dump_write(struct sk_buff *skb, unsigned long _state)
......@@ -309,3 +298,10 @@ int netlink_policy_dump_write(struct sk_buff *skb, unsigned long _state)
nla_nest_cancel(skb, policy);
return -ENOBUFS;
}
void netlink_policy_dump_free(unsigned long _state)
{
struct nl_policy_dump *state = (void *)_state;
kfree(state);
}
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