|
| 1 | +netfilter: nft_set_pipapo: walk over current view on netlink dump |
| 2 | + |
| 3 | +jira LE-2169 |
| 4 | +cve CVE-2024-27017 |
| 5 | +Rebuild_History Non-Buildable kernel-4.18.0-553.27.1.el8_10 |
| 6 | +commit-author Pablo Neira Ayuso < [email protected]> |
| 7 | +commit 29b359cf6d95fd60730533f7f10464e95bd17c73 |
| 8 | +Empty-Commit: Cherry-Pick Conflicts during history rebuild. |
| 9 | +Will be included in final tarball splat. Ref for failed cherry-pick at: |
| 10 | +ciq/ciq_backports/kernel-4.18.0-553.27.1.el8_10/29b359cf.failed |
| 11 | + |
| 12 | +The generation mask can be updated while netlink dump is in progress. |
| 13 | +The pipapo set backend walk iterator cannot rely on it to infer what |
| 14 | +view of the datastructure is to be used. Add notation to specify if user |
| 15 | +wants to read/update the set. |
| 16 | + |
| 17 | +Based on patch from Florian Westphal. |
| 18 | + |
| 19 | +Fixes: 2b84e215f874 ("netfilter: nft_set_pipapo: .walk does not deal with generations") |
| 20 | + Signed-off-by: Pablo Neira Ayuso < [email protected]> |
| 21 | +(cherry picked from commit 29b359cf6d95fd60730533f7f10464e95bd17c73) |
| 22 | + Signed-off-by: Jonathan Maple < [email protected]> |
| 23 | + |
| 24 | +# Conflicts: |
| 25 | +# include/net/netfilter/nf_tables.h |
| 26 | +# net/netfilter/nf_tables_api.c |
| 27 | +# net/netfilter/nft_set_pipapo.c |
| 28 | +diff --cc include/net/netfilter/nf_tables.h |
| 29 | +index dc1113771aec,3f1ed467f951..000000000000 |
| 30 | +--- a/include/net/netfilter/nf_tables.h |
| 31 | ++++ b/include/net/netfilter/nf_tables.h |
| 32 | +@@@ -246,9 -295,31 +246,30 @@@ struct nft_set_elem |
| 33 | + u32 buf[NFT_DATA_VALUE_MAXLEN / sizeof(u32)]; |
| 34 | + struct nft_data val; |
| 35 | + } key_end; |
| 36 | + - union { |
| 37 | + - u32 buf[NFT_DATA_VALUE_MAXLEN / sizeof(u32)]; |
| 38 | + - struct nft_data val; |
| 39 | + - } data; |
| 40 | + - struct nft_elem_priv *priv; |
| 41 | + + void *priv; |
| 42 | + }; |
| 43 | + |
| 44 | +++<<<<<<< HEAD |
| 45 | +++======= |
| 46 | ++ static inline void *nft_elem_priv_cast(const struct nft_elem_priv *priv) |
| 47 | ++ { |
| 48 | ++ return (void *)priv; |
| 49 | ++ } |
| 50 | ++ |
| 51 | ++ |
| 52 | ++ /** |
| 53 | ++ * enum nft_iter_type - nftables set iterator type |
| 54 | ++ * |
| 55 | ++ * @NFT_ITER_READ: read-only iteration over set elements |
| 56 | ++ * @NFT_ITER_UPDATE: iteration under mutex to update set element state |
| 57 | ++ */ |
| 58 | ++ enum nft_iter_type { |
| 59 | ++ NFT_ITER_UNSPEC, |
| 60 | ++ NFT_ITER_READ, |
| 61 | ++ NFT_ITER_UPDATE, |
| 62 | ++ }; |
| 63 | ++ |
| 64 | +++>>>>>>> 29b359cf6d95 (netfilter: nft_set_pipapo: walk over current view on netlink dump) |
| 65 | + struct nft_set; |
| 66 | + struct nft_set_iter { |
| 67 | + u8 genmask; |
| 68 | +diff --cc net/netfilter/nf_tables_api.c |
| 69 | +index a8d03cec29c3,a7a34db62ea9..000000000000 |
| 70 | +--- a/net/netfilter/nf_tables_api.c |
| 71 | ++++ b/net/netfilter/nf_tables_api.c |
| 72 | +@@@ -490,6 -583,59 +490,62 @@@ static int nft_trans_set_add(const stru |
| 73 | + return 0; |
| 74 | + } |
| 75 | + |
| 76 | +++<<<<<<< HEAD |
| 77 | +++======= |
| 78 | ++ static int nft_trans_set_add(const struct nft_ctx *ctx, int msg_type, |
| 79 | ++ struct nft_set *set) |
| 80 | ++ { |
| 81 | ++ return __nft_trans_set_add(ctx, msg_type, set, NULL); |
| 82 | ++ } |
| 83 | ++ |
| 84 | ++ static int nft_mapelem_deactivate(const struct nft_ctx *ctx, |
| 85 | ++ struct nft_set *set, |
| 86 | ++ const struct nft_set_iter *iter, |
| 87 | ++ struct nft_elem_priv *elem_priv) |
| 88 | ++ { |
| 89 | ++ nft_setelem_data_deactivate(ctx->net, set, elem_priv); |
| 90 | ++ |
| 91 | ++ return 0; |
| 92 | ++ } |
| 93 | ++ |
| 94 | ++ struct nft_set_elem_catchall { |
| 95 | ++ struct list_head list; |
| 96 | ++ struct rcu_head rcu; |
| 97 | ++ struct nft_elem_priv *elem; |
| 98 | ++ }; |
| 99 | ++ |
| 100 | ++ static void nft_map_catchall_deactivate(const struct nft_ctx *ctx, |
| 101 | ++ struct nft_set *set) |
| 102 | ++ { |
| 103 | ++ u8 genmask = nft_genmask_next(ctx->net); |
| 104 | ++ struct nft_set_elem_catchall *catchall; |
| 105 | ++ struct nft_set_ext *ext; |
| 106 | ++ |
| 107 | ++ list_for_each_entry(catchall, &set->catchall_list, list) { |
| 108 | ++ ext = nft_set_elem_ext(set, catchall->elem); |
| 109 | ++ if (!nft_set_elem_active(ext, genmask)) |
| 110 | ++ continue; |
| 111 | ++ |
| 112 | ++ nft_setelem_data_deactivate(ctx->net, set, catchall->elem); |
| 113 | ++ break; |
| 114 | ++ } |
| 115 | ++ } |
| 116 | ++ |
| 117 | ++ static void nft_map_deactivate(const struct nft_ctx *ctx, struct nft_set *set) |
| 118 | ++ { |
| 119 | ++ struct nft_set_iter iter = { |
| 120 | ++ .genmask = nft_genmask_next(ctx->net), |
| 121 | ++ .type = NFT_ITER_UPDATE, |
| 122 | ++ .fn = nft_mapelem_deactivate, |
| 123 | ++ }; |
| 124 | ++ |
| 125 | ++ set->ops->walk(ctx, set, &iter); |
| 126 | ++ WARN_ON_ONCE(iter.err); |
| 127 | ++ |
| 128 | ++ nft_map_catchall_deactivate(ctx, set); |
| 129 | ++ } |
| 130 | ++ |
| 131 | +++>>>>>>> 29b359cf6d95 (netfilter: nft_set_pipapo: walk over current view on netlink dump) |
| 132 | + static int nft_delset(const struct nft_ctx *ctx, struct nft_set *set) |
| 133 | + { |
| 134 | + int err; |
| 135 | +@@@ -4586,7 -5484,51 +4643,55 @@@ void nf_tables_unbind_set(const struct |
| 136 | + GFP_KERNEL); |
| 137 | + } |
| 138 | + } |
| 139 | +++<<<<<<< HEAD |
| 140 | + +EXPORT_SYMBOL_GPL(nf_tables_unbind_set); |
| 141 | +++======= |
| 142 | ++ |
| 143 | ++ static void nft_setelem_data_activate(const struct net *net, |
| 144 | ++ const struct nft_set *set, |
| 145 | ++ struct nft_elem_priv *elem_priv); |
| 146 | ++ |
| 147 | ++ static int nft_mapelem_activate(const struct nft_ctx *ctx, |
| 148 | ++ struct nft_set *set, |
| 149 | ++ const struct nft_set_iter *iter, |
| 150 | ++ struct nft_elem_priv *elem_priv) |
| 151 | ++ { |
| 152 | ++ nft_setelem_data_activate(ctx->net, set, elem_priv); |
| 153 | ++ |
| 154 | ++ return 0; |
| 155 | ++ } |
| 156 | ++ |
| 157 | ++ static void nft_map_catchall_activate(const struct nft_ctx *ctx, |
| 158 | ++ struct nft_set *set) |
| 159 | ++ { |
| 160 | ++ u8 genmask = nft_genmask_next(ctx->net); |
| 161 | ++ struct nft_set_elem_catchall *catchall; |
| 162 | ++ struct nft_set_ext *ext; |
| 163 | ++ |
| 164 | ++ list_for_each_entry(catchall, &set->catchall_list, list) { |
| 165 | ++ ext = nft_set_elem_ext(set, catchall->elem); |
| 166 | ++ if (!nft_set_elem_active(ext, genmask)) |
| 167 | ++ continue; |
| 168 | ++ |
| 169 | ++ nft_setelem_data_activate(ctx->net, set, catchall->elem); |
| 170 | ++ break; |
| 171 | ++ } |
| 172 | ++ } |
| 173 | ++ |
| 174 | ++ static void nft_map_activate(const struct nft_ctx *ctx, struct nft_set *set) |
| 175 | ++ { |
| 176 | ++ struct nft_set_iter iter = { |
| 177 | ++ .genmask = nft_genmask_next(ctx->net), |
| 178 | ++ .type = NFT_ITER_UPDATE, |
| 179 | ++ .fn = nft_mapelem_activate, |
| 180 | ++ }; |
| 181 | ++ |
| 182 | ++ set->ops->walk(ctx, set, &iter); |
| 183 | ++ WARN_ON_ONCE(iter.err); |
| 184 | ++ |
| 185 | ++ nft_map_catchall_activate(ctx, set); |
| 186 | ++ } |
| 187 | +++>>>>>>> 29b359cf6d95 (netfilter: nft_set_pipapo: walk over current view on netlink dump) |
| 188 | + |
| 189 | + void nf_tables_activate_set(const struct nft_ctx *ctx, struct nft_set *set) |
| 190 | + { |
| 191 | +@@@ -4925,7 -5893,9 +5030,8 @@@ static int nf_tables_dump_set(struct sk |
| 192 | + |
| 193 | + args.cb = cb; |
| 194 | + args.skb = skb; |
| 195 | + - args.reset = dump_ctx->reset; |
| 196 | + args.iter.genmask = nft_genmask_cur(net); |
| 197 | ++ args.iter.type = NFT_ITER_READ; |
| 198 | + args.iter.skip = cb->args[0]; |
| 199 | + args.iter.count = 0; |
| 200 | + args.iter.err = 0; |
| 201 | +@@@ -5978,13 -7335,72 +6084,76 @@@ static int nft_flush_set(const struct n |
| 202 | + return 0; |
| 203 | + } |
| 204 | + |
| 205 | + -static int __nft_set_catchall_flush(const struct nft_ctx *ctx, |
| 206 | + - struct nft_set *set, |
| 207 | + - struct nft_elem_priv *elem_priv) |
| 208 | + +static int nf_tables_delsetelem(struct net *net, struct sock *nlsk, |
| 209 | + + struct sk_buff *skb, const struct nlmsghdr *nlh, |
| 210 | + + const struct nlattr * const nla[], |
| 211 | + + struct netlink_ext_ack *extack) |
| 212 | + { |
| 213 | +++<<<<<<< HEAD |
| 214 | + + u8 genmask = nft_genmask_next(net); |
| 215 | +++======= |
| 216 | ++ struct nft_trans *trans; |
| 217 | ++ |
| 218 | ++ trans = nft_trans_alloc_gfp(ctx, NFT_MSG_DELSETELEM, |
| 219 | ++ sizeof(struct nft_trans_elem), GFP_KERNEL); |
| 220 | ++ if (!trans) |
| 221 | ++ return -ENOMEM; |
| 222 | ++ |
| 223 | ++ nft_setelem_data_deactivate(ctx->net, set, elem_priv); |
| 224 | ++ nft_trans_elem_set(trans) = set; |
| 225 | ++ nft_trans_elem_priv(trans) = elem_priv; |
| 226 | ++ nft_trans_commit_list_add_tail(ctx->net, trans); |
| 227 | ++ |
| 228 | ++ return 0; |
| 229 | ++ } |
| 230 | ++ |
| 231 | ++ static int nft_set_catchall_flush(const struct nft_ctx *ctx, |
| 232 | ++ struct nft_set *set) |
| 233 | ++ { |
| 234 | ++ u8 genmask = nft_genmask_next(ctx->net); |
| 235 | ++ struct nft_set_elem_catchall *catchall; |
| 236 | ++ struct nft_set_ext *ext; |
| 237 | ++ int ret = 0; |
| 238 | ++ |
| 239 | ++ list_for_each_entry_rcu(catchall, &set->catchall_list, list) { |
| 240 | ++ ext = nft_set_elem_ext(set, catchall->elem); |
| 241 | ++ if (!nft_set_elem_active(ext, genmask)) |
| 242 | ++ continue; |
| 243 | ++ |
| 244 | ++ ret = __nft_set_catchall_flush(ctx, set, catchall->elem); |
| 245 | ++ if (ret < 0) |
| 246 | ++ break; |
| 247 | ++ nft_set_elem_change_active(ctx->net, set, ext); |
| 248 | ++ } |
| 249 | ++ |
| 250 | ++ return ret; |
| 251 | ++ } |
| 252 | ++ |
| 253 | ++ static int nft_set_flush(struct nft_ctx *ctx, struct nft_set *set, u8 genmask) |
| 254 | ++ { |
| 255 | ++ struct nft_set_iter iter = { |
| 256 | ++ .genmask = genmask, |
| 257 | ++ .type = NFT_ITER_UPDATE, |
| 258 | ++ .fn = nft_setelem_flush, |
| 259 | ++ }; |
| 260 | ++ |
| 261 | ++ set->ops->walk(ctx, set, &iter); |
| 262 | ++ if (!iter.err) |
| 263 | ++ iter.err = nft_set_catchall_flush(ctx, set); |
| 264 | ++ |
| 265 | ++ return iter.err; |
| 266 | ++ } |
| 267 | ++ |
| 268 | ++ static int nf_tables_delsetelem(struct sk_buff *skb, |
| 269 | ++ const struct nfnl_info *info, |
| 270 | ++ const struct nlattr * const nla[]) |
| 271 | ++ { |
| 272 | ++ struct netlink_ext_ack *extack = info->extack; |
| 273 | ++ u8 genmask = nft_genmask_next(info->net); |
| 274 | ++ u8 family = info->nfmsg->nfgen_family; |
| 275 | ++ struct net *net = info->net; |
| 276 | +++>>>>>>> 29b359cf6d95 (netfilter: nft_set_pipapo: walk over current view on netlink dump) |
| 277 | + const struct nlattr *attr; |
| 278 | + - struct nft_table *table; |
| 279 | + struct nft_set *set; |
| 280 | + struct nft_ctx ctx; |
| 281 | + int rem, err = 0; |
| 282 | +diff --cc net/netfilter/nft_set_pipapo.c |
| 283 | +index 4b6a6667d72b,11e44e4dfb1f..000000000000 |
| 284 | +--- a/net/netfilter/nft_set_pipapo.c |
| 285 | ++++ b/net/netfilter/nft_set_pipapo.c |
| 286 | +@@@ -1908,13 -2115,14 +1908,21 @@@ static void nft_pipapo_walk(const struc |
| 287 | + struct nft_set_iter *iter) |
| 288 | + { |
| 289 | + struct nft_pipapo *priv = nft_set_priv(set); |
| 290 | +++<<<<<<< HEAD |
| 291 | + + struct net *net = read_pnet(&set->net); |
| 292 | + + struct nft_pipapo_match *m; |
| 293 | + + struct nft_pipapo_field *f; |
| 294 | + + int i, r; |
| 295 | +++======= |
| 296 | ++ const struct nft_pipapo_match *m; |
| 297 | ++ const struct nft_pipapo_field *f; |
| 298 | ++ unsigned int i, r; |
| 299 | +++>>>>>>> 29b359cf6d95 (netfilter: nft_set_pipapo: walk over current view on netlink dump) |
| 300 | ++ |
| 301 | ++ WARN_ON_ONCE(iter->type == NFT_ITER_UNSPEC); |
| 302 | + |
| 303 | + rcu_read_lock(); |
| 304 | +- if (iter->genmask == nft_genmask_cur(net)) |
| 305 | ++ if (iter->type == NFT_ITER_READ) |
| 306 | + m = rcu_dereference(priv->match); |
| 307 | + else |
| 308 | + m = priv->clone; |
| 309 | +* Unmerged path include/net/netfilter/nf_tables.h |
| 310 | +* Unmerged path net/netfilter/nf_tables_api.c |
| 311 | +* Unmerged path net/netfilter/nft_set_pipapo.c |
0 commit comments