netfilter: nf_tables: hook list memleak in flowtable deletion

After looking up for the flowtable hooks that need to be removed,
release the hook objects in the deletion list. The error path needs to
released these hook objects too.

Fixes: abadb2f865 ("netfilter: nf_tables: delete devices from flowtable")
Reported-by: syzbot+eb9d5924c51d6d59e094@syzkaller.appspotmail.com
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
Pablo Neira Ayuso 2020-06-10 19:29:16 +02:00
Родитель 6c2d2176a8
Коммит 3003055f50
1 изменённых файлов: 24 добавлений и 7 удалений

Просмотреть файл

@ -6550,12 +6550,22 @@ err1:
return err; return err;
} }
static void nft_flowtable_hook_release(struct nft_flowtable_hook *flowtable_hook)
{
struct nft_hook *this, *next;
list_for_each_entry_safe(this, next, &flowtable_hook->list, list) {
list_del(&this->list);
kfree(this);
}
}
static int nft_delflowtable_hook(struct nft_ctx *ctx, static int nft_delflowtable_hook(struct nft_ctx *ctx,
struct nft_flowtable *flowtable) struct nft_flowtable *flowtable)
{ {
const struct nlattr * const *nla = ctx->nla; const struct nlattr * const *nla = ctx->nla;
struct nft_flowtable_hook flowtable_hook; struct nft_flowtable_hook flowtable_hook;
struct nft_hook *this, *next, *hook; struct nft_hook *this, *hook;
struct nft_trans *trans; struct nft_trans *trans;
int err; int err;
@ -6564,33 +6574,40 @@ static int nft_delflowtable_hook(struct nft_ctx *ctx,
if (err < 0) if (err < 0)
return err; return err;
list_for_each_entry_safe(this, next, &flowtable_hook.list, list) { list_for_each_entry(this, &flowtable_hook.list, list) {
hook = nft_hook_list_find(&flowtable->hook_list, this); hook = nft_hook_list_find(&flowtable->hook_list, this);
if (!hook) { if (!hook) {
err = -ENOENT; err = -ENOENT;
goto err_flowtable_del_hook; goto err_flowtable_del_hook;
} }
hook->inactive = true; hook->inactive = true;
list_del(&this->list);
kfree(this);
} }
trans = nft_trans_alloc(ctx, NFT_MSG_DELFLOWTABLE, trans = nft_trans_alloc(ctx, NFT_MSG_DELFLOWTABLE,
sizeof(struct nft_trans_flowtable)); sizeof(struct nft_trans_flowtable));
if (!trans) if (!trans) {
return -ENOMEM; err = -ENOMEM;
goto err_flowtable_del_hook;
}
nft_trans_flowtable(trans) = flowtable; nft_trans_flowtable(trans) = flowtable;
nft_trans_flowtable_update(trans) = true; nft_trans_flowtable_update(trans) = true;
INIT_LIST_HEAD(&nft_trans_flowtable_hooks(trans)); INIT_LIST_HEAD(&nft_trans_flowtable_hooks(trans));
nft_flowtable_hook_release(&flowtable_hook);
list_add_tail(&trans->list, &ctx->net->nft.commit_list); list_add_tail(&trans->list, &ctx->net->nft.commit_list);
return 0; return 0;
err_flowtable_del_hook: err_flowtable_del_hook:
list_for_each_entry(hook, &flowtable_hook.list, list) list_for_each_entry(this, &flowtable_hook.list, list) {
hook = nft_hook_list_find(&flowtable->hook_list, this);
if (!hook)
break;
hook->inactive = false; hook->inactive = false;
}
nft_flowtable_hook_release(&flowtable_hook);
return err; return err;
} }