Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'nf-26-04-28' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf

Pablo Neira Ayuso says:

====================
Netfilter fixes for net

The following patchset contains Netfilter fixes for net:

1) IEEE1394 ARP payload contains no target hardware address in the
ARP packet. Apparently, arp_tables was never updated to deal with
IEEE1394 ARP properly. To deal with this, return no match in case
the target hardware address selector is used, either for inverse or
normal match. Moreover, arpt_mangle disallows mangling of the target
hardware and IP address because, it is not worth to adjust the
offset calculation to fix this, we suspect no users of arp_tables
for this family.

2) Use list_del_rcu() to delete device hooks in nf_tables, this hook
list is RCU protected, concurrent netlink dump readers can be
walking on this list, fix it by adding a helper function and use it
for consistency. From Florian Westphal.

3) Add list_splice_rcu(), this is useful for joining the local list of
new device hooks to the RCU protected hook list in chain and
flowtable. Reviewed by Paul E. McKenney.

4) Use list_splice_rcu() to publish the new device hooks in chain and
flowtable to fix concurrent netlink dump traversal.

5) Add a new hook transaction object to track device hook deletions.
The current approach moves device hooks to be deleted around during
the preparation phase, this breaks concurrent RCU reader via netlink
dump. This new hook transaction is combined with NFT_HOOK_REMOVE
flag to annotate hooks for removal in the preparation phase.

6) xt_policy inbound policy check in strict mode can lead to
out-of-bound access of the secpath array due to incorrect.
The iteration over the secpath needs to be reversed in the inbound
to check for the human readable policy, expecting inner in first
position and outer in second position, the secpath from inbound
actually stores outer in first position then in second position.
From Jiexun Wang.

7) Fix possible zero shift in nft_bitwise triggering UBSAN splat,
reject zero shift from control plane, from Kai Ma.

8) Replace simple_strtoul() in the conntrack SIP helper since it relies
on nul-terminated strings. From Florian Westphal.

* tag 'nf-26-04-28' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf:
netfilter: nf_conntrack_sip: don't use simple_strtoul
netfilter: reject zero shift in nft_bitwise
netfilter: xt_policy: fix strict mode inbound policy matching
netfilter: nf_tables: add hook transactions for device deletions
netfilter: nf_tables: join hook list via splice_list_rcu() in commit phase
rculist: add list_splice_rcu() for private lists
netfilter: nf_tables: use list_del_rcu for netlink hooks
netfilter: arp_tables: fix IEEE1394 ARP payload parsing
====================

Link: https://patch.msgid.link/20260428095840.51961-1-pablo@netfilter.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+412 -128
+29
include/linux/rculist.h
··· 261 261 old->prev = LIST_POISON2; 262 262 } 263 263 264 + static inline void __list_splice_rcu(struct list_head *list, 265 + struct list_head *prev, 266 + struct list_head *next) 267 + { 268 + struct list_head *first = list->next; 269 + struct list_head *last = list->prev; 270 + 271 + last->next = next; 272 + first->prev = prev; 273 + next->prev = last; 274 + rcu_assign_pointer(list_next_rcu(prev), first); 275 + } 276 + 277 + /** 278 + * list_splice_rcu - splice a non-RCU list into an RCU-protected list, 279 + * designed for stacks. 280 + * @list: the non RCU-protected list to splice 281 + * @head: the place in the existing RCU-protected list to splice 282 + * 283 + * The list pointed to by @head can be RCU-read traversed concurrently with 284 + * this function. 285 + */ 286 + static inline void list_splice_rcu(struct list_head *list, 287 + struct list_head *head) 288 + { 289 + if (!list_empty(list)) 290 + __list_splice_rcu(list, head, head->next); 291 + } 292 + 264 293 /** 265 294 * __list_splice_init_rcu - join an RCU-protected list into an existing list. 266 295 * @list: the RCU-protected list to splice
+13
include/net/netfilter/nf_tables.h
··· 1204 1204 struct u64_stats_sync syncp; 1205 1205 }; 1206 1206 1207 + #define NFT_HOOK_REMOVE (1 << 0) 1208 + 1207 1209 struct nft_hook { 1208 1210 struct list_head list; 1209 1211 struct list_head ops_list; 1210 1212 struct rcu_head rcu; 1211 1213 char ifname[IFNAMSIZ]; 1212 1214 u8 ifnamelen; 1215 + u8 flags; 1213 1216 }; 1214 1217 1215 1218 struct nf_hook_ops *nft_hook_find_ops(const struct nft_hook *hook, ··· 1665 1662 u16 flags; 1666 1663 u8 report:1; 1667 1664 u8 put_net:1; 1665 + }; 1666 + 1667 + /** 1668 + * struct nft_trans_hook - nf_tables hook update in transaction 1669 + * @list: used internally 1670 + * @hook: struct nft_hook with the device hook 1671 + */ 1672 + struct nft_trans_hook { 1673 + struct list_head list; 1674 + struct nft_hook *hook; 1668 1675 }; 1669 1676 1670 1677 /**
+15 -3
net/ipv4/netfilter/arp_tables.c
··· 110 110 arpptr += dev->addr_len; 111 111 memcpy(&src_ipaddr, arpptr, sizeof(u32)); 112 112 arpptr += sizeof(u32); 113 - tgt_devaddr = arpptr; 114 - arpptr += dev->addr_len; 113 + 114 + if (IS_ENABLED(CONFIG_FIREWIRE_NET) && dev->type == ARPHRD_IEEE1394) { 115 + if (unlikely(memchr_inv(arpinfo->tgt_devaddr.mask, 0, 116 + sizeof(arpinfo->tgt_devaddr.mask)))) 117 + return 0; 118 + 119 + tgt_devaddr = NULL; 120 + } else { 121 + tgt_devaddr = arpptr; 122 + arpptr += dev->addr_len; 123 + } 115 124 memcpy(&tgt_ipaddr, arpptr, sizeof(u32)); 116 125 117 126 if (NF_INVF(arpinfo, ARPT_INV_SRCDEVADDR, 118 127 arp_devaddr_compare(&arpinfo->src_devaddr, src_devaddr, 119 - dev->addr_len)) || 128 + dev->addr_len))) 129 + return 0; 130 + 131 + if (tgt_devaddr && 120 132 NF_INVF(arpinfo, ARPT_INV_TGTDEVADDR, 121 133 arp_devaddr_compare(&arpinfo->tgt_devaddr, tgt_devaddr, 122 134 dev->addr_len)))
+8
net/ipv4/netfilter/arpt_mangle.c
··· 40 40 } 41 41 arpptr += pln; 42 42 if (mangle->flags & ARPT_MANGLE_TDEV) { 43 + if (unlikely(IS_ENABLED(CONFIG_FIREWIRE_NET) && 44 + skb->dev->type == ARPHRD_IEEE1394)) 45 + return NF_DROP; 46 + 43 47 if (ARPT_DEV_ADDR_LEN_MAX < hln || 44 48 (arpptr + hln > skb_tail_pointer(skb))) 45 49 return NF_DROP; ··· 51 47 } 52 48 arpptr += hln; 53 49 if (mangle->flags & ARPT_MANGLE_TIP) { 50 + if (unlikely(IS_ENABLED(CONFIG_FIREWIRE_NET) && 51 + skb->dev->type == ARPHRD_IEEE1394)) 52 + return NF_DROP; 53 + 54 54 if (ARPT_MANGLE_ADDR_LEN_MAX < pln || 55 55 (arpptr + pln > skb_tail_pointer(skb))) 56 56 return NF_DROP;
+118 -34
net/netfilter/nf_conntrack_sip.c
··· 181 181 return 1; 182 182 } 183 183 184 + /* Parse optional port number after IP address. 185 + * Returns false on malformed input, true otherwise. 186 + * If port is non-NULL, stores parsed port in network byte order. 187 + * If no port is present, sets *port to default SIP port. 188 + */ 189 + static bool sip_parse_port(const char *dptr, const char **endp, 190 + const char *limit, __be16 *port) 191 + { 192 + unsigned int p = 0; 193 + int len = 0; 194 + 195 + if (dptr >= limit) 196 + return false; 197 + 198 + if (*dptr != ':') { 199 + if (port) 200 + *port = htons(SIP_PORT); 201 + if (endp) 202 + *endp = dptr; 203 + return true; 204 + } 205 + 206 + dptr++; /* skip ':' */ 207 + 208 + while (dptr < limit && isdigit(*dptr)) { 209 + p = p * 10 + (*dptr - '0'); 210 + dptr++; 211 + len++; 212 + if (len > 5) /* max "65535" */ 213 + return false; 214 + } 215 + 216 + if (len == 0) 217 + return false; 218 + 219 + /* reached limit while parsing port */ 220 + if (dptr >= limit) 221 + return false; 222 + 223 + if (p < 1024 || p > 65535) 224 + return false; 225 + 226 + if (port) 227 + *port = htons(p); 228 + 229 + if (endp) 230 + *endp = dptr; 231 + 232 + return true; 233 + } 234 + 184 235 /* skip ip address. returns its length. */ 185 236 static int epaddr_len(const struct nf_conn *ct, const char *dptr, 186 237 const char *limit, int *shift) ··· 244 193 return 0; 245 194 } 246 195 247 - /* Port number */ 248 - if (*dptr == ':') { 249 - dptr++; 250 - dptr += digits_len(ct, dptr, limit, shift); 251 - } 196 + if (!sip_parse_port(dptr, &dptr, limit, NULL)) 197 + return 0; 252 198 return dptr - aux; 253 199 } 254 200 ··· 276 228 return epaddr_len(ct, dptr, limit, shift); 277 229 } 278 230 231 + /* simple_strtoul stops after first non-number character. 232 + * But as we're not dealing with c-strings, we can't rely on 233 + * hitting \r,\n,\0 etc. before moving past end of buffer. 234 + * 235 + * This is a variant of simple_strtoul, but doesn't require 236 + * a c-string. 237 + * 238 + * If value exceeds UINT_MAX, 0 is returned. 239 + */ 240 + static unsigned int sip_strtouint(const char *cp, unsigned int len, char **endp) 241 + { 242 + const unsigned int max = sizeof("4294967295"); 243 + unsigned int olen = len; 244 + const char *s = cp; 245 + u64 result = 0; 246 + 247 + if (len > max) 248 + len = max; 249 + 250 + while (olen > 0 && isdigit(*s)) { 251 + unsigned int value; 252 + 253 + if (len == 0) 254 + goto err; 255 + 256 + value = *s - '0'; 257 + result = result * 10 + value; 258 + 259 + if (result > UINT_MAX) 260 + goto err; 261 + s++; 262 + len--; 263 + olen--; 264 + } 265 + 266 + if (endp) 267 + *endp = (char *)s; 268 + 269 + return result; 270 + err: 271 + if (endp) 272 + *endp = (char *)cp; 273 + return 0; 274 + } 275 + 279 276 /* Parse a SIP request line of the form: 280 277 * 281 278 * Request-Line = Method SP Request-URI SP SIP-Version CRLF ··· 334 241 { 335 242 const char *start = dptr, *limit = dptr + datalen, *end; 336 243 unsigned int mlen; 337 - unsigned int p; 338 244 int shift = 0; 339 245 340 246 /* Skip method and following whitespace */ ··· 359 267 360 268 if (!sip_parse_addr(ct, dptr, &end, addr, limit, true)) 361 269 return -1; 362 - if (end < limit && *end == ':') { 363 - end++; 364 - p = simple_strtoul(end, (char **)&end, 10); 365 - if (p < 1024 || p > 65535) 366 - return -1; 367 - *port = htons(p); 368 - } else 369 - *port = htons(SIP_PORT); 270 + if (!sip_parse_port(end, &end, limit, port)) 271 + return -1; 370 272 371 273 if (end == dptr) 372 274 return 0; ··· 595 509 union nf_inet_addr *addr, __be16 *port) 596 510 { 597 511 const char *c, *limit = dptr + datalen; 598 - unsigned int p; 599 512 int ret; 600 513 601 514 ret = ct_sip_walk_headers(ct, dptr, dataoff ? *dataoff : 0, datalen, ··· 605 520 606 521 if (!sip_parse_addr(ct, dptr + *matchoff, &c, addr, limit, true)) 607 522 return -1; 608 - if (*c == ':') { 609 - c++; 610 - p = simple_strtoul(c, (char **)&c, 10); 611 - if (p < 1024 || p > 65535) 612 - return -1; 613 - *port = htons(p); 614 - } else 615 - *port = htons(SIP_PORT); 523 + if (!sip_parse_port(c, &c, limit, port)) 524 + return -1; 616 525 617 526 if (dataoff) 618 527 *dataoff = c - dptr; ··· 688 609 return 0; 689 610 690 611 start += strlen(name); 691 - *val = simple_strtoul(start, &end, 0); 612 + *val = sip_strtouint(start, limit - start, (char **)&end); 692 613 if (start == end) 693 614 return -1; 694 615 if (matchoff && matchlen) { ··· 1143 1064 1144 1065 mediaoff = sdpoff; 1145 1066 for (i = 0; i < ARRAY_SIZE(sdp_media_types); ) { 1067 + char *end; 1068 + 1146 1069 if (ct_sip_get_sdp_header(ct, *dptr, mediaoff, *datalen, 1147 1070 SDP_HDR_MEDIA, SDP_HDR_UNSPEC, 1148 1071 &mediaoff, &medialen) <= 0) ··· 1160 1079 mediaoff += t->len; 1161 1080 medialen -= t->len; 1162 1081 1163 - port = simple_strtoul(*dptr + mediaoff, NULL, 10); 1164 - if (port == 0) 1082 + port = sip_strtouint(*dptr + mediaoff, *datalen - mediaoff, (char **)&end); 1083 + if (port == 0 || *dptr + mediaoff == end) 1165 1084 continue; 1166 1085 if (port < 1024 || port > 65535) { 1167 1086 nf_ct_helper_log(skb, ct, "wrong port %u", port); ··· 1335 1254 */ 1336 1255 if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_EXPIRES, 1337 1256 &matchoff, &matchlen) > 0) 1338 - expires = simple_strtoul(*dptr + matchoff, NULL, 10); 1257 + expires = sip_strtouint(*dptr + matchoff, *datalen - matchoff, NULL); 1339 1258 1340 1259 ret = ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen, 1341 1260 SIP_HDR_CONTACT, NULL, ··· 1439 1358 1440 1359 if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_EXPIRES, 1441 1360 &matchoff, &matchlen) > 0) 1442 - expires = simple_strtoul(*dptr + matchoff, NULL, 10); 1361 + expires = sip_strtouint(*dptr + matchoff, *datalen - matchoff, NULL); 1443 1362 1444 1363 while (1) { 1445 1364 unsigned int c_expires = expires; ··· 1499 1418 struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 1500 1419 unsigned int matchoff, matchlen, matchend; 1501 1420 unsigned int code, cseq, i; 1421 + char *end; 1502 1422 1503 1423 if (*datalen < strlen("SIP/2.0 200")) 1504 1424 return NF_ACCEPT; 1505 - code = simple_strtoul(*dptr + strlen("SIP/2.0 "), NULL, 10); 1425 + code = sip_strtouint(*dptr + strlen("SIP/2.0 "), 1426 + *datalen - strlen("SIP/2.0 "), NULL); 1506 1427 if (!code) { 1507 1428 nf_ct_helper_log(skb, ct, "cannot get code"); 1508 1429 return NF_DROP; ··· 1515 1432 nf_ct_helper_log(skb, ct, "cannot parse cseq"); 1516 1433 return NF_DROP; 1517 1434 } 1518 - cseq = simple_strtoul(*dptr + matchoff, NULL, 10); 1519 - if (!cseq && *(*dptr + matchoff) != '0') { 1435 + cseq = sip_strtouint(*dptr + matchoff, *datalen - matchoff, (char **)&end); 1436 + if (*dptr + matchoff == end) { 1520 1437 nf_ct_helper_log(skb, ct, "cannot get cseq"); 1521 1438 return NF_DROP; 1522 1439 } ··· 1565 1482 1566 1483 for (i = 0; i < ARRAY_SIZE(sip_handlers); i++) { 1567 1484 const struct sip_handler *handler; 1485 + char *end; 1568 1486 1569 1487 handler = &sip_handlers[i]; 1570 1488 if (handler->request == NULL) ··· 1582 1498 nf_ct_helper_log(skb, ct, "cannot parse cseq"); 1583 1499 return NF_DROP; 1584 1500 } 1585 - cseq = simple_strtoul(*dptr + matchoff, NULL, 10); 1586 - if (!cseq && *(*dptr + matchoff) != '0') { 1501 + cseq = sip_strtouint(*dptr + matchoff, *datalen - matchoff, (char **)&end); 1502 + if (*dptr + matchoff == end) { 1587 1503 nf_ct_helper_log(skb, ct, "cannot get cseq"); 1588 1504 return NF_DROP; 1589 1505 } ··· 1659 1575 &matchoff, &matchlen) <= 0) 1660 1576 break; 1661 1577 1662 - clen = simple_strtoul(dptr + matchoff, (char **)&end, 10); 1578 + clen = sip_strtouint(dptr + matchoff, datalen - matchoff, (char **)&end); 1663 1579 if (dptr + matchoff == end) 1664 1580 break; 1665 1581
+1
net/netfilter/nf_nat_sip.c
··· 246 246 if (ct_sip_parse_numerical_param(ct, *dptr, matchend, *datalen, 247 247 "rport=", &poff, &plen, 248 248 &n) > 0 && 249 + n >= 1024 && n <= 65535 && 249 250 htons(n) == ct->tuplehash[dir].tuple.dst.u.udp.port && 250 251 htons(n) != ct->tuplehash[!dir].tuple.src.u.udp.port) { 251 252 __be16 p = ct->tuplehash[!dir].tuple.src.u.udp.port;
+225 -89
net/netfilter/nf_tables_api.c
··· 374 374 call_rcu(&hook->rcu, __nft_netdev_hook_free_rcu); 375 375 } 376 376 377 + static void nft_netdev_hook_unlink_free_rcu(struct nft_hook *hook) 378 + { 379 + list_del_rcu(&hook->list); 380 + nft_netdev_hook_free_rcu(hook); 381 + } 382 + 383 + static void nft_trans_hook_destroy(struct nft_trans_hook *trans_hook) 384 + { 385 + list_del(&trans_hook->list); 386 + kfree(trans_hook); 387 + } 388 + 389 + static void nft_netdev_unregister_trans_hook(struct net *net, 390 + const struct nft_table *table, 391 + struct list_head *hook_list) 392 + { 393 + struct nft_trans_hook *trans_hook, *next; 394 + struct nf_hook_ops *ops; 395 + struct nft_hook *hook; 396 + 397 + list_for_each_entry_safe(trans_hook, next, hook_list, list) { 398 + hook = trans_hook->hook; 399 + 400 + if (!(table->flags & NFT_TABLE_F_DORMANT)) { 401 + list_for_each_entry(ops, &hook->ops_list, list) 402 + nf_unregister_net_hook(net, ops); 403 + } 404 + nft_netdev_hook_unlink_free_rcu(hook); 405 + nft_trans_hook_destroy(trans_hook); 406 + } 407 + } 408 + 377 409 static void nft_netdev_unregister_hooks(struct net *net, 378 410 struct list_head *hook_list, 379 411 bool release_netdev) ··· 416 384 list_for_each_entry_safe(hook, next, hook_list, list) { 417 385 list_for_each_entry(ops, &hook->ops_list, list) 418 386 nf_unregister_net_hook(net, ops); 419 - if (release_netdev) { 420 - list_del(&hook->list); 421 - nft_netdev_hook_free_rcu(hook); 422 - } 387 + if (release_netdev) 388 + nft_netdev_hook_unlink_free_rcu(hook); 423 389 } 424 390 } 425 391 ··· 1972 1942 return nla_put_string(skb, attr, hook->ifname); 1973 1943 } 1974 1944 1945 + struct nft_hook_dump_ctx { 1946 + struct nft_hook *first; 1947 + int n; 1948 + }; 1949 + 1950 + static int nft_dump_basechain_hook_one(struct sk_buff *skb, 1951 + struct nft_hook *hook, 1952 + struct nft_hook_dump_ctx *dump_ctx) 1953 + { 1954 + if (!dump_ctx->first) 1955 + dump_ctx->first = hook; 1956 + 1957 + if (nft_nla_put_hook_dev(skb, hook)) 1958 + return -1; 1959 + 1960 + dump_ctx->n++; 1961 + 1962 + return 0; 1963 + } 1964 + 1965 + static int nft_dump_basechain_hook_list(struct sk_buff *skb, 1966 + const struct net *net, 1967 + const struct list_head *hook_list, 1968 + struct nft_hook_dump_ctx *dump_ctx) 1969 + { 1970 + struct nft_hook *hook; 1971 + int err; 1972 + 1973 + list_for_each_entry_rcu(hook, hook_list, list, 1974 + lockdep_commit_lock_is_held(net)) { 1975 + err = nft_dump_basechain_hook_one(skb, hook, dump_ctx); 1976 + if (err < 0) 1977 + return err; 1978 + } 1979 + 1980 + return 0; 1981 + } 1982 + 1983 + static int nft_dump_basechain_trans_hook_list(struct sk_buff *skb, 1984 + const struct list_head *trans_hook_list, 1985 + struct nft_hook_dump_ctx *dump_ctx) 1986 + { 1987 + struct nft_trans_hook *trans_hook; 1988 + int err; 1989 + 1990 + list_for_each_entry(trans_hook, trans_hook_list, list) { 1991 + err = nft_dump_basechain_hook_one(skb, trans_hook->hook, dump_ctx); 1992 + if (err < 0) 1993 + return err; 1994 + } 1995 + 1996 + return 0; 1997 + } 1998 + 1975 1999 static int nft_dump_basechain_hook(struct sk_buff *skb, 1976 2000 const struct net *net, int family, 1977 2001 const struct nft_base_chain *basechain, 1978 - const struct list_head *hook_list) 2002 + const struct list_head *hook_list, 2003 + const struct list_head *trans_hook_list) 1979 2004 { 1980 2005 const struct nf_hook_ops *ops = &basechain->ops; 1981 - struct nft_hook *hook, *first = NULL; 2006 + struct nft_hook_dump_ctx dump_hook_ctx = {}; 1982 2007 struct nlattr *nest, *nest_devs; 1983 - int n = 0; 1984 2008 1985 2009 nest = nla_nest_start_noflag(skb, NFTA_CHAIN_HOOK); 1986 2010 if (nest == NULL) ··· 2049 1965 if (!nest_devs) 2050 1966 goto nla_put_failure; 2051 1967 2052 - if (!hook_list) 1968 + if (!hook_list && !trans_hook_list) 2053 1969 hook_list = &basechain->hook_list; 2054 1970 2055 - list_for_each_entry_rcu(hook, hook_list, list, 2056 - lockdep_commit_lock_is_held(net)) { 2057 - if (!first) 2058 - first = hook; 2059 - 2060 - if (nft_nla_put_hook_dev(skb, hook)) 2061 - goto nla_put_failure; 2062 - n++; 1971 + if (hook_list && 1972 + nft_dump_basechain_hook_list(skb, net, hook_list, &dump_hook_ctx)) { 1973 + goto nla_put_failure; 1974 + } else if (trans_hook_list && 1975 + nft_dump_basechain_trans_hook_list(skb, trans_hook_list, 1976 + &dump_hook_ctx)) { 1977 + goto nla_put_failure; 2063 1978 } 1979 + 2064 1980 nla_nest_end(skb, nest_devs); 2065 1981 2066 - if (n == 1 && 2067 - !hook_is_prefix(first) && 2068 - nla_put_string(skb, NFTA_HOOK_DEV, first->ifname)) 1982 + if (dump_hook_ctx.n == 1 && 1983 + !hook_is_prefix(dump_hook_ctx.first) && 1984 + nla_put_string(skb, NFTA_HOOK_DEV, dump_hook_ctx.first->ifname)) 2069 1985 goto nla_put_failure; 2070 1986 } 2071 1987 nla_nest_end(skb, nest); ··· 2079 1995 u32 portid, u32 seq, int event, u32 flags, 2080 1996 int family, const struct nft_table *table, 2081 1997 const struct nft_chain *chain, 2082 - const struct list_head *hook_list) 1998 + const struct list_head *hook_list, 1999 + const struct list_head *trans_hook_list) 2083 2000 { 2084 2001 struct nlmsghdr *nlh; 2085 2002 ··· 2096 2011 NFTA_CHAIN_PAD)) 2097 2012 goto nla_put_failure; 2098 2013 2099 - if (!hook_list && 2014 + if (!hook_list && !trans_hook_list && 2100 2015 (event == NFT_MSG_DELCHAIN || 2101 2016 event == NFT_MSG_DESTROYCHAIN)) { 2102 2017 nlmsg_end(skb, nlh); ··· 2107 2022 const struct nft_base_chain *basechain = nft_base_chain(chain); 2108 2023 struct nft_stats __percpu *stats; 2109 2024 2110 - if (nft_dump_basechain_hook(skb, net, family, basechain, hook_list)) 2025 + if (nft_dump_basechain_hook(skb, net, family, basechain, 2026 + hook_list, trans_hook_list)) 2111 2027 goto nla_put_failure; 2112 2028 2113 2029 if (nla_put_be32(skb, NFTA_CHAIN_POLICY, ··· 2144 2058 } 2145 2059 2146 2060 static void nf_tables_chain_notify(const struct nft_ctx *ctx, int event, 2147 - const struct list_head *hook_list) 2061 + const struct list_head *hook_list, 2062 + const struct list_head *trans_hook_list) 2148 2063 { 2149 2064 struct nftables_pernet *nft_net; 2150 2065 struct sk_buff *skb; ··· 2165 2078 2166 2079 err = nf_tables_fill_chain_info(skb, ctx->net, ctx->portid, ctx->seq, 2167 2080 event, flags, ctx->family, ctx->table, 2168 - ctx->chain, hook_list); 2081 + ctx->chain, hook_list, trans_hook_list); 2169 2082 if (err < 0) { 2170 2083 kfree_skb(skb); 2171 2084 goto err; ··· 2211 2124 NFT_MSG_NEWCHAIN, 2212 2125 NLM_F_MULTI, 2213 2126 table->family, table, 2214 - chain, NULL) < 0) 2127 + chain, NULL, NULL) < 0) 2215 2128 goto done; 2216 2129 2217 2130 nl_dump_check_consistent(cb, nlmsg_hdr(skb)); ··· 2265 2178 2266 2179 err = nf_tables_fill_chain_info(skb2, net, NETLINK_CB(skb).portid, 2267 2180 info->nlh->nlmsg_seq, NFT_MSG_NEWCHAIN, 2268 - 0, family, table, chain, NULL); 2181 + 0, family, table, chain, NULL, NULL); 2269 2182 if (err < 0) 2270 2183 goto err_fill_chain_info; 2271 2184 ··· 2358 2271 2359 2272 if (nft_base_chain_netdev(table->family, basechain->ops.hooknum)) { 2360 2273 list_for_each_entry_safe(hook, next, 2361 - &basechain->hook_list, list) { 2362 - list_del_rcu(&hook->list); 2363 - nft_netdev_hook_free_rcu(hook); 2364 - } 2274 + &basechain->hook_list, list) 2275 + nft_netdev_hook_unlink_free_rcu(hook); 2365 2276 } 2366 2277 module_put(basechain->type->owner); 2367 2278 if (rcu_access_pointer(basechain->stats)) { ··· 2428 2343 2429 2344 list_for_each_entry(hook, hook_list, list) { 2430 2345 if (!strncmp(hook->ifname, this->ifname, 2431 - min(hook->ifnamelen, this->ifnamelen))) 2346 + min(hook->ifnamelen, this->ifnamelen))) { 2347 + if (hook->flags & NFT_HOOK_REMOVE) 2348 + continue; 2349 + 2432 2350 return hook; 2351 + } 2433 2352 } 2434 2353 2435 2354 return NULL; ··· 3063 2974 list_for_each_entry(ops, &h->ops_list, list) 3064 2975 nf_unregister_net_hook(ctx->net, ops); 3065 2976 } 2977 + /* hook.list is on stack, no need for list_del_rcu() */ 3066 2978 list_del(&h->list); 3067 2979 nft_netdev_hook_free_rcu(h); 3068 2980 } ··· 3192 3102 return nf_tables_addchain(&ctx, family, policy, flags, extack); 3193 3103 } 3194 3104 3105 + static int nft_trans_delhook(struct nft_hook *hook, 3106 + struct list_head *del_list) 3107 + { 3108 + struct nft_trans_hook *trans_hook; 3109 + 3110 + trans_hook = kmalloc_obj(*trans_hook, GFP_KERNEL); 3111 + if (!trans_hook) 3112 + return -ENOMEM; 3113 + 3114 + trans_hook->hook = hook; 3115 + list_add_tail(&trans_hook->list, del_list); 3116 + hook->flags |= NFT_HOOK_REMOVE; 3117 + 3118 + return 0; 3119 + } 3120 + 3121 + static void nft_trans_delhook_abort(struct list_head *del_list) 3122 + { 3123 + struct nft_trans_hook *trans_hook, *next; 3124 + 3125 + list_for_each_entry_safe(trans_hook, next, del_list, list) { 3126 + trans_hook->hook->flags &= ~NFT_HOOK_REMOVE; 3127 + nft_trans_hook_destroy(trans_hook); 3128 + } 3129 + } 3130 + 3195 3131 static int nft_delchain_hook(struct nft_ctx *ctx, 3196 3132 struct nft_base_chain *basechain, 3197 3133 struct netlink_ext_ack *extack) ··· 3244 3128 err = -ENOENT; 3245 3129 goto err_chain_del_hook; 3246 3130 } 3247 - list_move(&hook->list, &chain_del_list); 3131 + if (nft_trans_delhook(hook, &chain_del_list) < 0) { 3132 + err = -ENOMEM; 3133 + goto err_chain_del_hook; 3134 + } 3248 3135 } 3249 3136 3250 3137 trans = nft_trans_alloc_chain(ctx, NFT_MSG_DELCHAIN); ··· 3267 3148 return 0; 3268 3149 3269 3150 err_chain_del_hook: 3270 - list_splice(&chain_del_list, &basechain->hook_list); 3151 + nft_trans_delhook_abort(&chain_del_list); 3271 3152 nft_chain_release_hook(&chain_hook); 3272 3153 3273 3154 return err; ··· 8971 8852 list_for_each_entry_safe(hook, next, hook_list, list) { 8972 8853 list_for_each_entry(ops, &hook->ops_list, list) 8973 8854 nft_unregister_flowtable_ops(net, flowtable, ops); 8974 - if (release_netdev) { 8975 - list_del(&hook->list); 8976 - nft_netdev_hook_free_rcu(hook); 8977 - } 8855 + if (release_netdev) 8856 + nft_netdev_hook_unlink_free_rcu(hook); 8978 8857 } 8979 8858 } 8980 8859 ··· 9043 8926 9044 8927 nft_unregister_flowtable_ops(net, flowtable, ops); 9045 8928 } 9046 - list_del_rcu(&hook->list); 9047 - nft_netdev_hook_free_rcu(hook); 8929 + nft_netdev_hook_unlink_free_rcu(hook); 9048 8930 } 9049 8931 9050 8932 return err; ··· 9053 8937 { 9054 8938 struct nft_hook *hook, *next; 9055 8939 9056 - list_for_each_entry_safe(hook, next, hook_list, list) { 9057 - list_del_rcu(&hook->list); 9058 - nft_netdev_hook_free_rcu(hook); 8940 + list_for_each_entry_safe(hook, next, hook_list, list) 8941 + nft_netdev_hook_unlink_free_rcu(hook); 8942 + } 8943 + 8944 + static void nft_flowtable_unregister_trans_hook(struct net *net, 8945 + struct nft_flowtable *flowtable, 8946 + struct list_head *hook_list) 8947 + { 8948 + struct nft_trans_hook *trans_hook, *next; 8949 + struct nf_hook_ops *ops; 8950 + struct nft_hook *hook; 8951 + 8952 + list_for_each_entry_safe(trans_hook, next, hook_list, list) { 8953 + hook = trans_hook->hook; 8954 + list_for_each_entry(ops, &hook->ops_list, list) 8955 + nft_unregister_flowtable_ops(net, flowtable, ops); 8956 + 8957 + nft_netdev_hook_unlink_free_rcu(hook); 8958 + nft_trans_hook_destroy(trans_hook); 9059 8959 } 9060 8960 } 9061 8961 ··· 9160 9028 nft_unregister_flowtable_ops(ctx->net, 9161 9029 flowtable, ops); 9162 9030 } 9163 - list_del_rcu(&hook->list); 9164 - nft_netdev_hook_free_rcu(hook); 9031 + nft_netdev_hook_unlink_free_rcu(hook); 9165 9032 } 9166 9033 9167 9034 return err; ··· 9333 9202 err = -ENOENT; 9334 9203 goto err_flowtable_del_hook; 9335 9204 } 9336 - list_move(&hook->list, &flowtable_del_list); 9205 + if (nft_trans_delhook(hook, &flowtable_del_list) < 0) { 9206 + err = -ENOMEM; 9207 + goto err_flowtable_del_hook; 9208 + } 9337 9209 } 9338 9210 9339 9211 trans = nft_trans_alloc(ctx, NFT_MSG_DELFLOWTABLE, ··· 9357 9223 return 0; 9358 9224 9359 9225 err_flowtable_del_hook: 9360 - list_splice(&flowtable_del_list, &flowtable->hook_list); 9226 + nft_trans_delhook_abort(&flowtable_del_list); 9361 9227 nft_flowtable_hook_release(&flowtable_hook); 9362 9228 9363 9229 return err; ··· 9422 9288 u32 portid, u32 seq, int event, 9423 9289 u32 flags, int family, 9424 9290 struct nft_flowtable *flowtable, 9425 - struct list_head *hook_list) 9291 + struct list_head *hook_list, 9292 + struct list_head *trans_hook_list) 9426 9293 { 9294 + struct nft_trans_hook *trans_hook; 9427 9295 struct nlattr *nest, *nest_devs; 9428 9296 struct nft_hook *hook; 9429 9297 struct nlmsghdr *nlh; ··· 9442 9306 NFTA_FLOWTABLE_PAD)) 9443 9307 goto nla_put_failure; 9444 9308 9445 - if (!hook_list && 9309 + if (!hook_list && !trans_hook_list && 9446 9310 (event == NFT_MSG_DELFLOWTABLE || 9447 9311 event == NFT_MSG_DESTROYFLOWTABLE)) { 9448 9312 nlmsg_end(skb, nlh); ··· 9464 9328 if (!nest_devs) 9465 9329 goto nla_put_failure; 9466 9330 9467 - if (!hook_list) 9331 + if (!hook_list && !trans_hook_list) 9468 9332 hook_list = &flowtable->hook_list; 9469 9333 9470 - list_for_each_entry_rcu(hook, hook_list, list, 9471 - lockdep_commit_lock_is_held(net)) { 9472 - if (nft_nla_put_hook_dev(skb, hook)) 9473 - goto nla_put_failure; 9334 + if (hook_list) { 9335 + list_for_each_entry_rcu(hook, hook_list, list, 9336 + lockdep_commit_lock_is_held(net)) { 9337 + if (nft_nla_put_hook_dev(skb, hook)) 9338 + goto nla_put_failure; 9339 + } 9340 + } else if (trans_hook_list) { 9341 + list_for_each_entry(trans_hook, trans_hook_list, list) { 9342 + if (nft_nla_put_hook_dev(skb, trans_hook->hook)) 9343 + goto nla_put_failure; 9344 + } 9474 9345 } 9475 9346 nla_nest_end(skb, nest_devs); 9476 9347 nla_nest_end(skb, nest); ··· 9531 9388 NFT_MSG_NEWFLOWTABLE, 9532 9389 NLM_F_MULTI | NLM_F_APPEND, 9533 9390 table->family, 9534 - flowtable, NULL) < 0) 9391 + flowtable, NULL, NULL) < 0) 9535 9392 goto done; 9536 9393 9537 9394 nl_dump_check_consistent(cb, nlmsg_hdr(skb)); ··· 9631 9488 err = nf_tables_fill_flowtable_info(skb2, net, NETLINK_CB(skb).portid, 9632 9489 info->nlh->nlmsg_seq, 9633 9490 NFT_MSG_NEWFLOWTABLE, 0, family, 9634 - flowtable, NULL); 9491 + flowtable, NULL, NULL); 9635 9492 if (err < 0) 9636 9493 goto err_fill_flowtable_info; 9637 9494 ··· 9644 9501 9645 9502 static void nf_tables_flowtable_notify(struct nft_ctx *ctx, 9646 9503 struct nft_flowtable *flowtable, 9647 - struct list_head *hook_list, int event) 9504 + struct list_head *hook_list, 9505 + struct list_head *trans_hook_list, 9506 + int event) 9648 9507 { 9649 9508 struct nftables_pernet *nft_net = nft_pernet(ctx->net); 9650 9509 struct sk_buff *skb; ··· 9666 9521 9667 9522 err = nf_tables_fill_flowtable_info(skb, ctx->net, ctx->portid, 9668 9523 ctx->seq, event, flags, 9669 - ctx->family, flowtable, hook_list); 9524 + ctx->family, flowtable, 9525 + hook_list, trans_hook_list); 9670 9526 if (err < 0) { 9671 9527 kfree_skb(skb); 9672 9528 goto err; ··· 9681 9535 9682 9536 static void nf_tables_flowtable_destroy(struct nft_flowtable *flowtable) 9683 9537 { 9684 - struct nft_hook *hook, *next; 9685 - 9686 9538 flowtable->data.type->free(&flowtable->data); 9687 - list_for_each_entry_safe(hook, next, &flowtable->hook_list, list) { 9688 - list_del_rcu(&hook->list); 9689 - nft_netdev_hook_free_rcu(hook); 9690 - } 9539 + nft_hooks_destroy(&flowtable->hook_list); 9691 9540 kfree(flowtable->name); 9692 9541 module_put(flowtable->data.type->owner); 9693 9542 kfree(flowtable); ··· 10201 10060 break; 10202 10061 case NFT_MSG_DELCHAIN: 10203 10062 case NFT_MSG_DESTROYCHAIN: 10204 - if (nft_trans_chain_update(trans)) 10205 - nft_hooks_destroy(&nft_trans_chain_hooks(trans)); 10206 - else 10063 + if (!nft_trans_chain_update(trans)) 10207 10064 nf_tables_chain_destroy(nft_trans_chain(trans)); 10208 10065 break; 10209 10066 case NFT_MSG_DELRULE: ··· 10222 10083 break; 10223 10084 case NFT_MSG_DELFLOWTABLE: 10224 10085 case NFT_MSG_DESTROYFLOWTABLE: 10225 - if (nft_trans_flowtable_update(trans)) 10226 - nft_hooks_destroy(&nft_trans_flowtable_hooks(trans)); 10227 - else 10086 + if (!nft_trans_flowtable_update(trans)) 10228 10087 nf_tables_flowtable_destroy(nft_trans_flowtable(trans)); 10229 10088 break; 10230 10089 } ··· 10982 10845 if (nft_trans_chain_update(trans)) { 10983 10846 nft_chain_commit_update(nft_trans_container_chain(trans)); 10984 10847 nf_tables_chain_notify(&ctx, NFT_MSG_NEWCHAIN, 10985 - &nft_trans_chain_hooks(trans)); 10986 - list_splice(&nft_trans_chain_hooks(trans), 10987 - &nft_trans_basechain(trans)->hook_list); 10848 + &nft_trans_chain_hooks(trans), NULL); 10849 + list_splice_rcu(&nft_trans_chain_hooks(trans), 10850 + &nft_trans_basechain(trans)->hook_list); 10988 10851 /* trans destroyed after rcu grace period */ 10989 10852 } else { 10990 10853 nft_chain_commit_drop_policy(nft_trans_container_chain(trans)); 10991 10854 nft_clear(net, nft_trans_chain(trans)); 10992 - nf_tables_chain_notify(&ctx, NFT_MSG_NEWCHAIN, NULL); 10855 + nf_tables_chain_notify(&ctx, NFT_MSG_NEWCHAIN, NULL, NULL); 10993 10856 nft_trans_destroy(trans); 10994 10857 } 10995 10858 break; 10996 10859 case NFT_MSG_DELCHAIN: 10997 10860 case NFT_MSG_DESTROYCHAIN: 10998 10861 if (nft_trans_chain_update(trans)) { 10999 - nf_tables_chain_notify(&ctx, NFT_MSG_DELCHAIN, 10862 + nf_tables_chain_notify(&ctx, NFT_MSG_DELCHAIN, NULL, 11000 10863 &nft_trans_chain_hooks(trans)); 11001 - if (!(table->flags & NFT_TABLE_F_DORMANT)) { 11002 - nft_netdev_unregister_hooks(net, 11003 - &nft_trans_chain_hooks(trans), 11004 - true); 11005 - } 10864 + nft_netdev_unregister_trans_hook(net, table, 10865 + &nft_trans_chain_hooks(trans)); 11006 10866 } else { 11007 10867 nft_chain_del(nft_trans_chain(trans)); 11008 10868 nf_tables_chain_notify(&ctx, NFT_MSG_DELCHAIN, 11009 - NULL); 10869 + NULL, NULL); 11010 10870 nf_tables_unregister_hook(ctx.net, ctx.table, 11011 10871 nft_trans_chain(trans)); 11012 10872 } ··· 11109 10975 nf_tables_flowtable_notify(&ctx, 11110 10976 nft_trans_flowtable(trans), 11111 10977 &nft_trans_flowtable_hooks(trans), 10978 + NULL, 11112 10979 NFT_MSG_NEWFLOWTABLE); 11113 - list_splice(&nft_trans_flowtable_hooks(trans), 11114 - &nft_trans_flowtable(trans)->hook_list); 10980 + list_splice_rcu(&nft_trans_flowtable_hooks(trans), 10981 + &nft_trans_flowtable(trans)->hook_list); 11115 10982 } else { 11116 10983 nft_clear(net, nft_trans_flowtable(trans)); 11117 10984 nf_tables_flowtable_notify(&ctx, 11118 10985 nft_trans_flowtable(trans), 10986 + NULL, 11119 10987 NULL, 11120 10988 NFT_MSG_NEWFLOWTABLE); 11121 10989 } ··· 11128 10992 if (nft_trans_flowtable_update(trans)) { 11129 10993 nf_tables_flowtable_notify(&ctx, 11130 10994 nft_trans_flowtable(trans), 10995 + NULL, 11131 10996 &nft_trans_flowtable_hooks(trans), 11132 10997 trans->msg_type); 11133 - nft_unregister_flowtable_net_hooks(net, 11134 - nft_trans_flowtable(trans), 11135 - &nft_trans_flowtable_hooks(trans)); 10998 + nft_flowtable_unregister_trans_hook(net, 10999 + nft_trans_flowtable(trans), 11000 + &nft_trans_flowtable_hooks(trans)); 11136 11001 } else { 11137 11002 list_del_rcu(&nft_trans_flowtable(trans)->list); 11138 11003 nf_tables_flowtable_notify(&ctx, 11139 11004 nft_trans_flowtable(trans), 11005 + NULL, 11140 11006 NULL, 11141 11007 trans->msg_type); 11142 11008 nft_unregister_flowtable_net_hooks(net, ··· 11303 11165 case NFT_MSG_DELCHAIN: 11304 11166 case NFT_MSG_DESTROYCHAIN: 11305 11167 if (nft_trans_chain_update(trans)) { 11306 - list_splice(&nft_trans_chain_hooks(trans), 11307 - &nft_trans_basechain(trans)->hook_list); 11168 + nft_trans_delhook_abort(&nft_trans_chain_hooks(trans)); 11308 11169 } else { 11309 11170 nft_use_inc_restore(&table->use); 11310 11171 nft_clear(trans->net, nft_trans_chain(trans)); ··· 11417 11280 case NFT_MSG_DELFLOWTABLE: 11418 11281 case NFT_MSG_DESTROYFLOWTABLE: 11419 11282 if (nft_trans_flowtable_update(trans)) { 11420 - list_splice(&nft_trans_flowtable_hooks(trans), 11421 - &nft_trans_flowtable(trans)->hook_list); 11283 + nft_trans_delhook_abort(&nft_trans_flowtable_hooks(trans)); 11422 11284 } else { 11423 11285 nft_use_inc_restore(&table->use); 11424 11286 nft_clear(trans->net, nft_trans_flowtable(trans));
+2 -1
net/netfilter/nft_bitwise.c
··· 196 196 if (err < 0) 197 197 return err; 198 198 199 - if (priv->data.data[0] >= BITS_PER_TYPE(u32)) { 199 + if (!priv->data.data[0] || 200 + priv->data.data[0] >= BITS_PER_TYPE(u32)) { 200 201 nft_data_release(&priv->data, desc.type); 201 202 return -EINVAL; 202 203 }
+1 -1
net/netfilter/xt_policy.c
··· 63 63 return 0; 64 64 65 65 for (i = sp->len - 1; i >= 0; i--) { 66 - pos = strict ? i - sp->len + 1 : 0; 66 + pos = strict ? sp->len - i - 1 : 0; 67 67 if (pos >= info->len) 68 68 return 0; 69 69 e = &info->pol[pos];