]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - releases/5.0.11/netfilter-nf_tables-bogus-ebusy-when-deleting-set-af.patch
4.14-stable patches
[thirdparty/kernel/stable-queue.git] / releases / 5.0.11 / netfilter-nf_tables-bogus-ebusy-when-deleting-set-af.patch
1 From cde4a3e910919f5b01be7dfa728ee735fa8f9937 Mon Sep 17 00:00:00 2001
2 From: Pablo Neira Ayuso <pablo@netfilter.org>
3 Date: Fri, 8 Mar 2019 15:30:03 +0100
4 Subject: netfilter: nf_tables: bogus EBUSY when deleting set after flush
5 MIME-Version: 1.0
6 Content-Type: text/plain; charset=UTF-8
7 Content-Transfer-Encoding: 8bit
8
9 [ Upstream commit 273fe3f1006ea5ebc63d6729e43e8e45e32b256a ]
10
11 Set deletion after flush coming in the same batch results in EBUSY. Add
12 set use counter to track the number of references to this set from
13 rules. We cannot rely on the list of bindings for this since such list
14 is still populated from the preparation phase.
15
16 Reported-by: Václav Zindulka <vaclav.zindulka@tlapnet.cz>
17 Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
18 Signed-off-by: Sasha Levin <sashal@kernel.org>
19 ---
20 include/net/netfilter/nf_tables.h | 6 ++++++
21 net/netfilter/nf_tables_api.c | 28 +++++++++++++++++++++++++++-
22 net/netfilter/nft_dynset.c | 13 +++++++++----
23 net/netfilter/nft_lookup.c | 13 +++++++++----
24 net/netfilter/nft_objref.c | 13 +++++++++----
25 5 files changed, 60 insertions(+), 13 deletions(-)
26
27 diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
28 index 0612439909dc..9e0b9ecb43db 100644
29 --- a/include/net/netfilter/nf_tables.h
30 +++ b/include/net/netfilter/nf_tables.h
31 @@ -382,6 +382,7 @@ void nft_unregister_set(struct nft_set_type *type);
32 * @dtype: data type (verdict or numeric type defined by userspace)
33 * @objtype: object type (see NFT_OBJECT_* definitions)
34 * @size: maximum set size
35 + * @use: number of rules references to this set
36 * @nelems: number of elements
37 * @ndeact: number of deactivated elements queued for removal
38 * @timeout: default timeout value in jiffies
39 @@ -407,6 +408,7 @@ struct nft_set {
40 u32 dtype;
41 u32 objtype;
42 u32 size;
43 + u32 use;
44 atomic_t nelems;
45 u32 ndeact;
46 u64 timeout;
47 @@ -467,6 +469,10 @@ struct nft_set_binding {
48 u32 flags;
49 };
50
51 +enum nft_trans_phase;
52 +void nf_tables_deactivate_set(const struct nft_ctx *ctx, struct nft_set *set,
53 + struct nft_set_binding *binding,
54 + enum nft_trans_phase phase);
55 int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
56 struct nft_set_binding *binding);
57 void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
58 diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
59 index acb124ce92ec..e2aac80f9b7b 100644
60 --- a/net/netfilter/nf_tables_api.c
61 +++ b/net/netfilter/nf_tables_api.c
62 @@ -3624,6 +3624,9 @@ err1:
63
64 static void nft_set_destroy(struct nft_set *set)
65 {
66 + if (WARN_ON(set->use > 0))
67 + return;
68 +
69 set->ops->destroy(set);
70 module_put(to_set_type(set->ops)->owner);
71 kfree(set->name);
72 @@ -3664,7 +3667,7 @@ static int nf_tables_delset(struct net *net, struct sock *nlsk,
73 NL_SET_BAD_ATTR(extack, attr);
74 return PTR_ERR(set);
75 }
76 - if (!list_empty(&set->bindings) ||
77 + if (set->use ||
78 (nlh->nlmsg_flags & NLM_F_NONREC && atomic_read(&set->nelems) > 0)) {
79 NL_SET_BAD_ATTR(extack, attr);
80 return -EBUSY;
81 @@ -3694,6 +3697,9 @@ int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
82 struct nft_set_binding *i;
83 struct nft_set_iter iter;
84
85 + if (set->use == UINT_MAX)
86 + return -EOVERFLOW;
87 +
88 if (!list_empty(&set->bindings) && nft_set_is_anonymous(set))
89 return -EBUSY;
90
91 @@ -3721,6 +3727,7 @@ bind:
92 binding->chain = ctx->chain;
93 list_add_tail_rcu(&binding->list, &set->bindings);
94 nft_set_trans_bind(ctx, set);
95 + set->use++;
96
97 return 0;
98 }
99 @@ -3740,6 +3747,25 @@ void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
100 }
101 EXPORT_SYMBOL_GPL(nf_tables_unbind_set);
102
103 +void nf_tables_deactivate_set(const struct nft_ctx *ctx, struct nft_set *set,
104 + struct nft_set_binding *binding,
105 + enum nft_trans_phase phase)
106 +{
107 + switch (phase) {
108 + case NFT_TRANS_PREPARE:
109 + set->use--;
110 + return;
111 + case NFT_TRANS_ABORT:
112 + case NFT_TRANS_RELEASE:
113 + set->use--;
114 + /* fall through */
115 + default:
116 + nf_tables_unbind_set(ctx, set, binding,
117 + phase == NFT_TRANS_COMMIT);
118 + }
119 +}
120 +EXPORT_SYMBOL_GPL(nf_tables_deactivate_set);
121 +
122 void nf_tables_destroy_set(const struct nft_ctx *ctx, struct nft_set *set)
123 {
124 if (list_empty(&set->bindings) && nft_set_is_anonymous(set))
125 diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c
126 index f1172f99752b..eb7f9a5f2aeb 100644
127 --- a/net/netfilter/nft_dynset.c
128 +++ b/net/netfilter/nft_dynset.c
129 @@ -241,11 +241,15 @@ static void nft_dynset_deactivate(const struct nft_ctx *ctx,
130 {
131 struct nft_dynset *priv = nft_expr_priv(expr);
132
133 - if (phase == NFT_TRANS_PREPARE)
134 - return;
135 + nf_tables_deactivate_set(ctx, priv->set, &priv->binding, phase);
136 +}
137 +
138 +static void nft_dynset_activate(const struct nft_ctx *ctx,
139 + const struct nft_expr *expr)
140 +{
141 + struct nft_dynset *priv = nft_expr_priv(expr);
142
143 - nf_tables_unbind_set(ctx, priv->set, &priv->binding,
144 - phase == NFT_TRANS_COMMIT);
145 + priv->set->use++;
146 }
147
148 static void nft_dynset_destroy(const struct nft_ctx *ctx,
149 @@ -293,6 +297,7 @@ static const struct nft_expr_ops nft_dynset_ops = {
150 .eval = nft_dynset_eval,
151 .init = nft_dynset_init,
152 .destroy = nft_dynset_destroy,
153 + .activate = nft_dynset_activate,
154 .deactivate = nft_dynset_deactivate,
155 .dump = nft_dynset_dump,
156 };
157 diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c
158 index 14496da5141d..161c3451a747 100644
159 --- a/net/netfilter/nft_lookup.c
160 +++ b/net/netfilter/nft_lookup.c
161 @@ -127,11 +127,15 @@ static void nft_lookup_deactivate(const struct nft_ctx *ctx,
162 {
163 struct nft_lookup *priv = nft_expr_priv(expr);
164
165 - if (phase == NFT_TRANS_PREPARE)
166 - return;
167 + nf_tables_deactivate_set(ctx, priv->set, &priv->binding, phase);
168 +}
169 +
170 +static void nft_lookup_activate(const struct nft_ctx *ctx,
171 + const struct nft_expr *expr)
172 +{
173 + struct nft_lookup *priv = nft_expr_priv(expr);
174
175 - nf_tables_unbind_set(ctx, priv->set, &priv->binding,
176 - phase == NFT_TRANS_COMMIT);
177 + priv->set->use++;
178 }
179
180 static void nft_lookup_destroy(const struct nft_ctx *ctx,
181 @@ -222,6 +226,7 @@ static const struct nft_expr_ops nft_lookup_ops = {
182 .size = NFT_EXPR_SIZE(sizeof(struct nft_lookup)),
183 .eval = nft_lookup_eval,
184 .init = nft_lookup_init,
185 + .activate = nft_lookup_activate,
186 .deactivate = nft_lookup_deactivate,
187 .destroy = nft_lookup_destroy,
188 .dump = nft_lookup_dump,
189 diff --git a/net/netfilter/nft_objref.c b/net/netfilter/nft_objref.c
190 index ae178e914486..d8737c115257 100644
191 --- a/net/netfilter/nft_objref.c
192 +++ b/net/netfilter/nft_objref.c
193 @@ -161,11 +161,15 @@ static void nft_objref_map_deactivate(const struct nft_ctx *ctx,
194 {
195 struct nft_objref_map *priv = nft_expr_priv(expr);
196
197 - if (phase == NFT_TRANS_PREPARE)
198 - return;
199 + nf_tables_deactivate_set(ctx, priv->set, &priv->binding, phase);
200 +}
201 +
202 +static void nft_objref_map_activate(const struct nft_ctx *ctx,
203 + const struct nft_expr *expr)
204 +{
205 + struct nft_objref_map *priv = nft_expr_priv(expr);
206
207 - nf_tables_unbind_set(ctx, priv->set, &priv->binding,
208 - phase == NFT_TRANS_COMMIT);
209 + priv->set->use++;
210 }
211
212 static void nft_objref_map_destroy(const struct nft_ctx *ctx,
213 @@ -182,6 +186,7 @@ static const struct nft_expr_ops nft_objref_map_ops = {
214 .size = NFT_EXPR_SIZE(sizeof(struct nft_objref_map)),
215 .eval = nft_objref_map_eval,
216 .init = nft_objref_map_init,
217 + .activate = nft_objref_map_activate,
218 .deactivate = nft_objref_map_deactivate,
219 .destroy = nft_objref_map_destroy,
220 .dump = nft_objref_map_dump,
221 --
222 2.19.1
223