netlink: fix policy dump leak

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

Fixes: d07dcf9aad ("netlink: add infrastructure to expose policies to userspace")
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Reviewed-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Johannes Berg 2020-10-02 09:46:04 +02:00 committed by David S. Miller
parent ef9da46dde
commit a95bc734e6
3 changed files with 20 additions and 16 deletions

View File

@ -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

View File

@ -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,
},
};

View File

@ -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;
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;
}
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);
}