Add a helper function to wrap data read from register and use it. Signed-off-by: Pablo Neira Ayuso <pablo@xxxxxxxxxxxxx> --- include/net/netfilter/nf_tables.h | 31 +++++++------ net/bridge/netfilter/nft_meta_bridge.c | 14 ++++-- net/ipv4/netfilter/nft_dup_ipv4.c | 26 ++++++++--- net/ipv6/netfilter/nft_dup_ipv6.c | 20 +++++++-- net/netfilter/nf_tables_core.c | 17 +++++-- net/netfilter/nft_bitwise.c | 7 ++- net/netfilter/nft_byteorder.c | 16 +++++-- net/netfilter/nft_cmp.c | 8 +++- net/netfilter/nft_ct.c | 27 ++++++++---- net/netfilter/nft_dup_netdev.c | 12 ++++- net/netfilter/nft_dynset.c | 27 +++++++++--- net/netfilter/nft_exthdr.c | 15 +++++-- net/netfilter/nft_fwd_netdev.c | 27 +++++++++--- net/netfilter/nft_hash.c | 10 ++++- net/netfilter/nft_lookup.c | 9 +++- net/netfilter/nft_masq.c | 18 ++++++-- net/netfilter/nft_meta.c | 21 ++++++--- net/netfilter/nft_nat.c | 61 ++++++++++++++++++-------- net/netfilter/nft_objref.c | 17 ++++--- net/netfilter/nft_payload.c | 6 ++- net/netfilter/nft_queue.c | 13 ++++-- net/netfilter/nft_range.c | 13 +++++- net/netfilter/nft_redir.c | 18 ++++++-- net/netfilter/nft_tproxy.c | 49 +++++++++++++++++---- 24 files changed, 366 insertions(+), 116 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index ac95b704cd70..ea258bfc6506 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -141,24 +141,34 @@ struct nft_regs_track { * garbage values. */ -static inline u8 nft_reg_load8(const u32 *sreg) +static inline const u8 *nft_reg_load_u8(const struct nft_regs *regs, u32 sreg) { - return *(u8 *)sreg; + return (const u8 *)®s->data[sreg]; } -static inline u16 nft_reg_load16(const u32 *sreg) +static inline const u16 *nft_reg_load_u16(const struct nft_regs *regs, u32 sreg) { - return *(u16 *)sreg; + return (const u16 *)®s->data[sreg]; } -static inline __be16 nft_reg_load_be16(const u32 *sreg) +static inline const u32 *nft_reg_load_u32(const struct nft_regs *regs, u32 sreg) { - return (__force __be16)nft_reg_load16(sreg); + return ®s->data[sreg]; } -static inline __be32 nft_reg_load_be32(const u32 *sreg) +static inline const void *nft_reg_load(const struct nft_regs *regs, u32 sreg, u8 len) { - return *(__force __be32 *)sreg; + return (const void *)®s->data[sreg]; +} + +static inline const __be16 *nft_reg_load_be16(const struct nft_regs *regs, u32 sreg) +{ + return (__force __be16 *)nft_reg_load_u16(regs, sreg); +} + +static inline const __be32 *nft_reg_load_be32(const struct nft_regs *regs, u32 sreg) +{ + return (__force __be32 *)nft_reg_load_u32(regs, sreg); } static inline void nft_reg_store_u8(struct nft_regs *regs, u32 dreg, u8 value) @@ -238,11 +248,6 @@ static inline void nft_reg_reset(struct nft_regs *regs, u32 dreg, u8 len) memset(dest, 0, len); } -static inline u64 nft_reg_load64(const u32 *sreg) -{ - return get_unaligned((u64 *)sreg); -} - static inline void nft_data_copy(struct nft_regs *regs, const struct nft_data *src, u32 dreg, unsigned int len) diff --git a/net/bridge/netfilter/nft_meta_bridge.c b/net/bridge/netfilter/nft_meta_bridge.c index c96a0f08bb3e..5e592b4df642 100644 --- a/net/bridge/netfilter/nft_meta_bridge.c +++ b/net/bridge/netfilter/nft_meta_bridge.c @@ -109,18 +109,24 @@ static void nft_meta_bridge_set_eval(const struct nft_expr *expr, const struct nft_pktinfo *pkt) { const struct nft_meta *meta = nft_expr_priv(expr); - u32 *sreg = ®s->data[meta->sreg]; struct sk_buff *skb = pkt->skb; - u8 value8; + const u8 *value8; switch (meta->key) { case NFT_META_BRI_BROUTE: - value8 = nft_reg_load8(sreg); - BR_INPUT_SKB_CB(skb)->br_netfilter_broute = !!value8; + value8 = nft_reg_load_u8(regs, meta->sreg); + if (!value8) + goto err; + + BR_INPUT_SKB_CB(skb)->br_netfilter_broute = !!*value8; break; default: nft_meta_set_eval(expr, regs, pkt); } + + return; +err: + regs->verdict.code = NFT_BREAK; } static int nft_meta_bridge_set_init(const struct nft_ctx *ctx, diff --git a/net/ipv4/netfilter/nft_dup_ipv4.c b/net/ipv4/netfilter/nft_dup_ipv4.c index a522c3a3be52..559636e4de52 100644 --- a/net/ipv4/netfilter/nft_dup_ipv4.c +++ b/net/ipv4/netfilter/nft_dup_ipv4.c @@ -22,12 +22,26 @@ static void nft_dup_ipv4_eval(const struct nft_expr *expr, const struct nft_pktinfo *pkt) { struct nft_dup_ipv4 *priv = nft_expr_priv(expr); - struct in_addr gw = { - .s_addr = (__force __be32)regs->data[priv->sreg_addr], - }; - int oif = priv->sreg_dev ? regs->data[priv->sreg_dev] : -1; - - nf_dup_ipv4(nft_net(pkt), pkt->skb, nft_hook(pkt), &gw, oif); + const int *oif = NULL; + struct in_addr gw; + const u32 *addr; + + addr = nft_reg_load_be32(regs, priv->sreg_addr); + if (!addr) + goto err; + + if (priv->sreg_dev) { + oif = nft_reg_load_u32(regs, priv->sreg_dev); + if (!oif) + goto err; + } + + gw.s_addr = *addr; + nf_dup_ipv4(nft_net(pkt), pkt->skb, nft_hook(pkt), &gw, oif ? *oif : -1); + + return; +err: + regs->verdict.code = NFT_BREAK; } static int nft_dup_ipv4_init(const struct nft_ctx *ctx, diff --git a/net/ipv6/netfilter/nft_dup_ipv6.c b/net/ipv6/netfilter/nft_dup_ipv6.c index c82f3fdd4a65..9323ff81fd12 100644 --- a/net/ipv6/netfilter/nft_dup_ipv6.c +++ b/net/ipv6/netfilter/nft_dup_ipv6.c @@ -22,10 +22,24 @@ static void nft_dup_ipv6_eval(const struct nft_expr *expr, const struct nft_pktinfo *pkt) { struct nft_dup_ipv6 *priv = nft_expr_priv(expr); - struct in6_addr *gw = (struct in6_addr *)®s->data[priv->sreg_addr]; - int oif = priv->sreg_dev ? regs->data[priv->sreg_dev] : -1; + const struct in6_addr *gw; + const u32 *oif; - nf_dup_ipv6(nft_net(pkt), pkt->skb, nft_hook(pkt), gw, oif); + gw = nft_reg_load(regs, priv->sreg_addr, sizeof(struct in6_addr)); + if (!gw) + goto err; + + if (priv->sreg_dev) { + oif = nft_reg_load_be32(regs, priv->sreg_dev); + if (!oif) + goto err; + } + + nf_dup_ipv6(nft_net(pkt), pkt->skb, nft_hook(pkt), gw, *oif); + + return; +err: + regs->verdict.code = NFT_BREAK; } static int nft_dup_ipv6_init(const struct nft_ctx *ctx, diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c index 1ee950368b6c..812d580b61cf 100644 --- a/net/netfilter/nf_tables_core.c +++ b/net/netfilter/nf_tables_core.c @@ -78,7 +78,12 @@ static void nft_bitwise_fast_eval(const struct nft_expr *expr, struct nft_regs *regs) { const struct nft_bitwise_fast_expr *priv = nft_expr_priv(expr); - u32 *src = ®s->data[priv->sreg]; + const u32 *src = nft_reg_load_u32(regs, priv->sreg); + + if (!src) { + regs->verdict.code = NFT_BREAK; + return; + } nft_reg_store_u32(regs, priv->dreg, (*src & priv->mask) ^ priv->xor); } @@ -87,9 +92,11 @@ static void nft_cmp_fast_eval(const struct nft_expr *expr, struct nft_regs *regs) { const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr); + const u32 *src = nft_reg_load_u32(regs, priv->sreg); - if (((regs->data[priv->sreg] & priv->mask) == priv->data) ^ priv->inv) + if (src && (((*src & priv->mask) == priv->data) ^ priv->inv)) return; + regs->verdict.code = NFT_BREAK; } @@ -97,13 +104,15 @@ static void nft_cmp16_fast_eval(const struct nft_expr *expr, struct nft_regs *regs) { const struct nft_cmp16_fast_expr *priv = nft_expr_priv(expr); - const u64 *reg_data = (const u64 *)®s->data[priv->sreg]; + const u64 *reg_data = nft_reg_load(regs, priv->sreg, 16); const u64 *mask = (const u64 *)&priv->mask; const u64 *data = (const u64 *)&priv->data; - if (((reg_data[0] & mask[0]) == data[0] && + if (reg_data && + ((reg_data[0] & mask[0]) == data[0] && ((reg_data[1] & mask[1]) == data[1])) ^ priv->inv) return; + regs->verdict.code = NFT_BREAK; } diff --git a/net/netfilter/nft_bitwise.c b/net/netfilter/nft_bitwise.c index d3c066aa399a..66a73df4b484 100644 --- a/net/netfilter/nft_bitwise.c +++ b/net/netfilter/nft_bitwise.c @@ -69,7 +69,12 @@ void nft_bitwise_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) { const struct nft_bitwise *priv = nft_expr_priv(expr); - const u32 *src = ®s->data[priv->sreg]; + const u32 *src = nft_reg_load(regs, priv->sreg, priv->len); + + if (!src) { + regs->verdict.code = NFT_BREAK; + return; + } switch (priv->op) { case NFT_BITWISE_BOOL: diff --git a/net/netfilter/nft_byteorder.c b/net/netfilter/nft_byteorder.c index 7c9c32706d10..494acf7ac993 100644 --- a/net/netfilter/nft_byteorder.c +++ b/net/netfilter/nft_byteorder.c @@ -28,10 +28,14 @@ void nft_byteorder_eval(const struct nft_expr *expr, const struct nft_pktinfo *pkt) { const struct nft_byteorder *priv = nft_expr_priv(expr); - u32 *src = ®s->data[priv->sreg]; - union { u32 u32; u16 u16; } *s; + const union { u32 u32; u16 u16; } *s; + const u32 *src; unsigned int i; + src = nft_reg_load(regs, priv->sreg, priv->len); + if (!src) + goto err; + s = (void *)src; switch (priv->size) { @@ -41,7 +45,7 @@ void nft_byteorder_eval(const struct nft_expr *expr, switch (priv->op) { case NFT_BYTEORDER_NTOH: for (i = 0; i < priv->len / 8; i++) { - src64 = nft_reg_load64(&src[i]); + src64 = get_unaligned((u64 *)&src[i]); nft_reg_store_u64(regs, priv->dreg, be64_to_cpu((__force __be64)src64)); } @@ -49,7 +53,7 @@ void nft_byteorder_eval(const struct nft_expr *expr, case NFT_BYTEORDER_HTON: for (i = 0; i < priv->len / 8; i++) { src64 = (__force __u64) - cpu_to_be64(nft_reg_load64(&src[i])); + cpu_to_be64(get_unaligned((u64 *)&src[i])); nft_reg_store_u64(regs, priv->dreg, src64); } break; @@ -82,6 +86,10 @@ void nft_byteorder_eval(const struct nft_expr *expr, break; } } + + return; +err: + regs->verdict.code = NFT_BREAK; } static const struct nla_policy nft_byteorder_policy[NFTA_BYTEORDER_MAX + 1] = { diff --git a/net/netfilter/nft_cmp.c b/net/netfilter/nft_cmp.c index 6eb21a4f5698..a4659597c6a7 100644 --- a/net/netfilter/nft_cmp.c +++ b/net/netfilter/nft_cmp.c @@ -28,9 +28,13 @@ void nft_cmp_eval(const struct nft_expr *expr, const struct nft_pktinfo *pkt) { const struct nft_cmp_expr *priv = nft_expr_priv(expr); + const u32 *src = nft_reg_load(regs, priv->sreg, priv->len); int d; - d = memcmp(®s->data[priv->sreg], &priv->data, priv->len); + if (!src) + goto mismatch; + + d = memcmp(src, &priv->data, priv->len); switch (priv->op) { case NFT_CMP_EQ: if (d != 0) @@ -167,7 +171,7 @@ static int __nft_cmp_offload(struct nft_offload_ctx *ctx, if (reg->key == FLOW_DISSECTOR_KEY_META && reg->offset == offsetof(struct nft_flow_key, meta.ingress_iftype) && - nft_reg_load16(priv->data.data) != ARPHRD_ETHER) + *(u16 *)priv->data.data != ARPHRD_ETHER) return -EOPNOTSUPP; nft_offload_update_dependency(ctx, &priv->data, reg->len); diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c index 8477f1b636bc..12f45c38905e 100644 --- a/net/netfilter/nft_ct.c +++ b/net/netfilter/nft_ct.c @@ -243,16 +243,18 @@ static void nft_ct_set_zone_eval(const struct nft_expr *expr, { struct nf_conntrack_zone zone = { .dir = NF_CT_DEFAULT_ZONE_DIR }; const struct nft_ct *priv = nft_expr_priv(expr); + const u16 *value = nft_reg_load_u16(regs, priv->sreg); struct sk_buff *skb = pkt->skb; enum ip_conntrack_info ctinfo; - u16 value = nft_reg_load16(®s->data[priv->sreg]); struct nf_conn *ct; ct = nf_ct_get(skb, &ctinfo); - if (ct) /* already tracked */ + if (!value || ct) { /* already tracked */ + regs->verdict.code = NFT_BREAK; return; + } - zone.id = value; + zone.id = *value; switch (priv->dir) { case IP_CT_DIR_ORIGINAL: @@ -292,11 +294,18 @@ static void nft_ct_set_eval(const struct nft_expr *expr, const struct nft_ct *priv = nft_expr_priv(expr); struct sk_buff *skb = pkt->skb; #if defined(CONFIG_NF_CONNTRACK_MARK) || defined(CONFIG_NF_CONNTRACK_SECMARK) - u32 value = regs->data[priv->sreg]; + const u32 *pvalue; + u32 value; #endif enum ip_conntrack_info ctinfo; struct nf_conn *ct; + pvalue = nft_reg_load(regs, priv->sreg, priv->len); + if (!pvalue) + goto err; + + value = *pvalue; + ct = nf_ct_get(skb, &ctinfo); if (ct == NULL || nf_ct_is_template(ct)) return; @@ -320,16 +329,14 @@ static void nft_ct_set_eval(const struct nft_expr *expr, #endif #ifdef CONFIG_NF_CONNTRACK_LABELS case NFT_CT_LABELS: - nf_connlabels_replace(ct, - ®s->data[priv->sreg], - ®s->data[priv->sreg], + nf_connlabels_replace(ct, pvalue, pvalue, NF_CT_LABELS_MAX_SIZE / sizeof(u32)); break; #endif #ifdef CONFIG_NF_CONNTRACK_EVENTS case NFT_CT_EVENTMASK: { struct nf_conntrack_ecache *e = nf_ct_ecache_find(ct); - u32 ctmask = regs->data[priv->sreg]; + u32 ctmask = value; if (e) { if (e->ctmask != ctmask) @@ -345,6 +352,10 @@ static void nft_ct_set_eval(const struct nft_expr *expr, default: break; } + + return; +err: + regs->verdict.code = NFT_BREAK; } static const struct nla_policy nft_ct_policy[NFTA_CT_MAX + 1] = { diff --git a/net/netfilter/nft_dup_netdev.c b/net/netfilter/nft_dup_netdev.c index e5739a59ebf1..75b6e1ad5f0f 100644 --- a/net/netfilter/nft_dup_netdev.c +++ b/net/netfilter/nft_dup_netdev.c @@ -22,9 +22,17 @@ static void nft_dup_netdev_eval(const struct nft_expr *expr, const struct nft_pktinfo *pkt) { struct nft_dup_netdev *priv = nft_expr_priv(expr); - int oif = regs->data[priv->sreg_dev]; + const u32 *oif; - nf_dup_netdev_egress(pkt, oif); + oif = nft_reg_load_u32(regs, priv->sreg_dev); + if (!oif) + goto err; + + nf_dup_netdev_egress(pkt, *oif); + + return; +err: + regs->verdict.code = NFT_BREAK; } static const struct nla_policy nft_dup_netdev_policy[NFTA_DUP_MAX + 1] = { diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c index 274579b1696e..e2ac29142921 100644 --- a/net/netfilter/nft_dynset.c +++ b/net/netfilter/nft_dynset.c @@ -48,17 +48,26 @@ static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr, struct nft_regs *regs) { const struct nft_dynset *priv = nft_expr_priv(expr); + const u32 *src_key, *src_data = NULL; struct nft_set_ext *ext; u64 timeout; void *elem; + src_key = nft_reg_load(regs, priv->sreg_key, set->klen); + if (!src_key) + return NULL; + + if (priv->set->flags & NFT_SET_MAP) { + src_data = nft_reg_load(regs, priv->sreg_data, set->dlen); + if (!src_data) + return NULL; + } + if (!atomic_add_unless(&set->nelems, 1, set->size)) return NULL; timeout = priv->timeout ? : set->timeout; - elem = nft_set_elem_init(set, &priv->tmpl, - ®s->data[priv->sreg_key], NULL, - ®s->data[priv->sreg_data], + elem = nft_set_elem_init(set, &priv->tmpl, src_key, NULL, src_data, timeout, 0, GFP_ATOMIC); if (IS_ERR(elem)) goto err1; @@ -83,15 +92,21 @@ void nft_dynset_eval(const struct nft_expr *expr, const struct nft_dynset *priv = nft_expr_priv(expr); struct nft_set *set = priv->set; const struct nft_set_ext *ext; + const u32 *src; u64 timeout; + src = nft_reg_load(regs, priv->sreg_key, set->klen); + if (!src) { + regs->verdict.code = NFT_BREAK; + return; + } + if (priv->op == NFT_DYNSET_OP_DELETE) { - set->ops->delete(set, ®s->data[priv->sreg_key]); + set->ops->delete(set, src); return; } - if (set->ops->update(set, ®s->data[priv->sreg_key], nft_dynset_new, - expr, regs, &ext)) { + if (set->ops->update(set, src, nft_dynset_new, expr, regs, &ext)) { if (priv->op == NFT_DYNSET_OP_UPDATE && nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) { timeout = priv->timeout ? : set->timeout; diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c index 4af9af63a604..0eade4035e0a 100644 --- a/net/netfilter/nft_exthdr.c +++ b/net/netfilter/nft_exthdr.c @@ -228,6 +228,8 @@ static void nft_exthdr_tcp_set_eval(const struct nft_expr *expr, u8 buff[sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE]; struct nft_exthdr *priv = nft_expr_priv(expr); unsigned int i, optl, tcphdr_len, offset; + const __be16 *src16; + const __be32 *src32; struct tcphdr *tcph; u8 *opt; @@ -263,9 +265,12 @@ static void nft_exthdr_tcp_set_eval(const struct nft_expr *expr, switch (priv->len) { case 2: + src16 = nft_reg_load_be16(regs, priv->sreg); + if (!src16) + goto err; + old.v16 = (__force __be16)get_unaligned((u16 *)(opt + offset)); - new.v16 = (__force __be16)nft_reg_load16( - ®s->data[priv->sreg]); + new.v16 = *src16; switch (priv->type) { case TCPOPT_MSS: @@ -283,7 +288,11 @@ static void nft_exthdr_tcp_set_eval(const struct nft_expr *expr, old.v16, new.v16, false); break; case 4: - new.v32 = nft_reg_load_be32(®s->data[priv->sreg]); + src32 = nft_reg_load_be32(regs, priv->sreg); + if (!src32) + goto err; + + new.v32 = *src32; old.v32 = (__force __be32)get_unaligned((u32 *)(opt + offset)); if (old.v32 == new.v32) diff --git a/net/netfilter/nft_fwd_netdev.c b/net/netfilter/nft_fwd_netdev.c index 7b9d4d1bd17c..235e899a00db 100644 --- a/net/netfilter/nft_fwd_netdev.c +++ b/net/netfilter/nft_fwd_netdev.c @@ -26,15 +26,23 @@ static void nft_fwd_netdev_eval(const struct nft_expr *expr, const struct nft_pktinfo *pkt) { struct nft_fwd_netdev *priv = nft_expr_priv(expr); - int oif = regs->data[priv->sreg_dev]; struct sk_buff *skb = pkt->skb; + const u32 *oif; + + oif = nft_reg_load_u32(regs, priv->sreg_dev); + if (!oif) + goto err; /* This is used by ifb only. */ skb->skb_iif = skb->dev->ifindex; skb_set_redirected(skb, nft_hook(pkt) == NF_NETDEV_INGRESS); - nf_fwd_netdev_egress(pkt, oif); + nf_fwd_netdev_egress(pkt, *oif); regs->verdict.code = NF_STOLEN; + + return; +err: + regs->verdict.code = NFT_BREAK; } static const struct nla_policy nft_fwd_netdev_policy[NFTA_FWD_MAX + 1] = { @@ -89,6 +97,7 @@ struct nft_fwd_neigh { u8 sreg_dev; u8 sreg_addr; u8 nfproto; + u8 addr_len; }; static void nft_fwd_neigh_eval(const struct nft_expr *expr, @@ -96,12 +105,20 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr, const struct nft_pktinfo *pkt) { struct nft_fwd_neigh *priv = nft_expr_priv(expr); - void *addr = ®s->data[priv->sreg_addr]; - int oif = regs->data[priv->sreg_dev]; unsigned int verdict = NF_STOLEN; struct sk_buff *skb = pkt->skb; struct net_device *dev; + const void *addr; int neigh_table; + const u32 *oif; + + oif = nft_reg_load_u32(regs, priv->sreg_dev); + addr = nft_reg_load(regs, priv->sreg_addr, priv->addr_len); + + if (!oif || !addr) { + verdict = NFT_BREAK; + goto out; + } switch (priv->nfproto) { case NFPROTO_IPV4: { @@ -141,7 +158,7 @@ static void nft_fwd_neigh_eval(const struct nft_expr *expr, goto out; } - dev = dev_get_by_index_rcu(nft_net(pkt), oif); + dev = dev_get_by_index_rcu(nft_net(pkt), *oif); if (dev == NULL) return; diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c index 8bcf9e9aa550..cca6a620214c 100644 --- a/net/netfilter/nft_hash.c +++ b/net/netfilter/nft_hash.c @@ -28,13 +28,21 @@ static void nft_jhash_eval(const struct nft_expr *expr, const struct nft_pktinfo *pkt) { struct nft_jhash *priv = nft_expr_priv(expr); - const void *data = ®s->data[priv->sreg]; + const void *data; u32 h; + data = nft_reg_load(regs, priv->sreg, priv->len); + if (!data) + goto err; + h = reciprocal_scale(jhash(data, priv->len, priv->seed), priv->modulus); nft_reg_store_u32(regs, priv->dreg, h + priv->offset); + + return; +err: + regs->verdict.code = NFT_BREAK; } struct nft_symhash { diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c index 3e3c20e42385..f48704187a94 100644 --- a/net/netfilter/nft_lookup.c +++ b/net/netfilter/nft_lookup.c @@ -62,9 +62,16 @@ void nft_lookup_eval(const struct nft_expr *expr, const struct nft_set *set = priv->set; const struct nft_set_ext *ext = NULL; const struct net *net = nft_net(pkt); + const u32 *src; bool found; - found = nft_set_do_lookup(net, set, ®s->data[priv->sreg], &ext) ^ + src = nft_reg_load(regs, priv->sreg, set->klen); + if (!src) { + regs->verdict.code = NFT_BREAK; + return; + } + + found = nft_set_do_lookup(net, set, src, &ext) ^ priv->invert; if (!found) { ext = nft_set_catchall_lookup(net, set); diff --git a/net/netfilter/nft_masq.c b/net/netfilter/nft_masq.c index b115d77fbbc7..8c327088c3f9 100644 --- a/net/netfilter/nft_masq.c +++ b/net/netfilter/nft_masq.c @@ -101,15 +101,21 @@ static void nft_masq_eval(const struct nft_expr *expr, const struct nft_pktinfo *pkt) { const struct nft_masq *priv = nft_expr_priv(expr); + const __be16 *src_proto_min, *src_proto_max; struct nf_nat_range2 range; + if (priv->sreg_proto_min) { + src_proto_min = nft_reg_load_be16(regs, priv->sreg_proto_min); + src_proto_max = nft_reg_load_be16(regs, priv->sreg_proto_max); + if (!src_proto_min || !src_proto_max) + goto err; + } + memset(&range, 0, sizeof(range)); range.flags = priv->flags; if (priv->sreg_proto_min) { - range.min_proto.all = (__force __be16) - nft_reg_load16(®s->data[priv->sreg_proto_min]); - range.max_proto.all = (__force __be16) - nft_reg_load16(®s->data[priv->sreg_proto_max]); + range.min_proto.all = *src_proto_min; + range.max_proto.all = *src_proto_max; } switch (nft_pf(pkt)) { @@ -129,6 +135,10 @@ static void nft_masq_eval(const struct nft_expr *expr, WARN_ON_ONCE(1); break; } + + return; +err: + regs->verdict.code = NFT_BREAK; } static void diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index 05b98b39a132..5ec80b7a0289 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -443,19 +443,22 @@ void nft_meta_set_eval(const struct nft_expr *expr, { const struct nft_meta *meta = nft_expr_priv(expr); struct sk_buff *skb = pkt->skb; - u32 *sreg = ®s->data[meta->sreg]; - u32 value = *sreg; + const u32 *src; u8 value8; + src = nft_reg_load(regs, meta->sreg, meta->len); + if (!src) + goto err; + switch (meta->key) { case NFT_META_MARK: - skb->mark = value; + skb->mark = *src; break; case NFT_META_PRIORITY: - skb->priority = value; + skb->priority = *src; break; case NFT_META_PKTTYPE: - value8 = nft_reg_load8(sreg); + value8 = *(u8 *)src; if (skb->pkt_type != value8 && skb_pkt_type_ok(value8) && @@ -463,18 +466,22 @@ void nft_meta_set_eval(const struct nft_expr *expr, skb->pkt_type = value8; break; case NFT_META_NFTRACE: - value8 = nft_reg_load8(sreg); + value8 = *(u8 *)src; skb->nf_trace = !!value8; break; #ifdef CONFIG_NETWORK_SECMARK case NFT_META_SECMARK: - skb->secmark = value; + skb->secmark = *src; break; #endif default: WARN_ON(1); } + + return; +err: + regs->verdict.code = NFT_BREAK; } EXPORT_SYMBOL_GPL(nft_meta_set_eval); diff --git a/net/netfilter/nft_nat.c b/net/netfilter/nft_nat.c index 5c29915ab028..8b3b75282549 100644 --- a/net/netfilter/nft_nat.c +++ b/net/netfilter/nft_nat.c @@ -27,37 +27,52 @@ struct nft_nat { u8 sreg_proto_max; enum nf_nat_manip_type type:8; u8 family; + u8 addr_len; u16 flags; }; -static void nft_nat_setup_addr(struct nf_nat_range2 *range, - const struct nft_regs *regs, - const struct nft_nat *priv) +static int nft_nat_setup_addr(struct nf_nat_range2 *range, + const struct nft_regs *regs, + const struct nft_nat *priv) { + const __be32 *src_addr_min, *src_addr_max; + + src_addr_min = nft_reg_load(regs, priv->sreg_addr_min, priv->addr_len); + src_addr_max = nft_reg_load(regs, priv->sreg_addr_max, priv->addr_len); + if (!src_addr_min || !src_addr_max) + return -1; + switch (priv->family) { case AF_INET: - range->min_addr.ip = (__force __be32) - regs->data[priv->sreg_addr_min]; - range->max_addr.ip = (__force __be32) - regs->data[priv->sreg_addr_max]; + range->min_addr.ip = *src_addr_min; + range->max_addr.ip = *src_addr_max; break; case AF_INET6: - memcpy(range->min_addr.ip6, ®s->data[priv->sreg_addr_min], + memcpy(range->min_addr.ip6, src_addr_min, sizeof(range->min_addr.ip6)); - memcpy(range->max_addr.ip6, ®s->data[priv->sreg_addr_max], + memcpy(range->max_addr.ip6, src_addr_max, sizeof(range->max_addr.ip6)); break; } + + return 0; } -static void nft_nat_setup_proto(struct nf_nat_range2 *range, - const struct nft_regs *regs, - const struct nft_nat *priv) +static int nft_nat_setup_proto(struct nf_nat_range2 *range, + const struct nft_regs *regs, + const struct nft_nat *priv) { - range->min_proto.all = (__force __be16) - nft_reg_load16(®s->data[priv->sreg_proto_min]); - range->max_proto.all = (__force __be16) - nft_reg_load16(®s->data[priv->sreg_proto_max]); + const __be16 *src_proto_min, *src_proto_max; + + src_proto_min = nft_reg_load_be16(regs, priv->sreg_proto_min); + src_proto_max = nft_reg_load_be16(regs, priv->sreg_proto_max); + if (!src_proto_min || !src_proto_max) + return -1; + + range->min_proto.all = *src_proto_min; + range->max_proto.all = *src_proto_max; + + return 0; } static void nft_nat_setup_netmap(struct nf_nat_range2 *range, @@ -112,17 +127,24 @@ static void nft_nat_eval(const struct nft_expr *expr, memset(&range, 0, sizeof(range)); if (priv->sreg_addr_min) { - nft_nat_setup_addr(&range, regs, priv); + if (nft_nat_setup_addr(&range, regs, priv) < 0) + goto err; + if (priv->flags & NF_NAT_RANGE_NETMAP) nft_nat_setup_netmap(&range, pkt, priv); } - if (priv->sreg_proto_min) - nft_nat_setup_proto(&range, regs, priv); + if (priv->sreg_proto_min && + nft_nat_setup_proto(&range, regs, priv) < 0) + goto err; range.flags = priv->flags; regs->verdict.code = nf_nat_setup_info(ct, &range, priv->type); + + return; +err: + regs->verdict.code = NFT_BREAK; } static const struct nla_policy nft_nat_policy[NFTA_NAT_MAX + 1] = { @@ -206,6 +228,7 @@ static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr, break; } priv->family = family; + priv->addr_len = alen; if (tb[NFTA_NAT_REG_ADDR_MIN]) { err = nft_parse_register_load(tb[NFTA_NAT_REG_ADDR_MIN], diff --git a/net/netfilter/nft_objref.c b/net/netfilter/nft_objref.c index cb37169608ba..9b1c85ab9d04 100644 --- a/net/netfilter/nft_objref.c +++ b/net/netfilter/nft_objref.c @@ -109,18 +109,25 @@ void nft_objref_map_eval(const struct nft_expr *expr, struct net *net = nft_net(pkt); const struct nft_set_ext *ext; struct nft_object *obj; + const u32 *src; bool found; - found = nft_set_do_lookup(net, set, ®s->data[priv->sreg], &ext); + src = nft_reg_load(regs, priv->sreg, set->klen); + if (!src) + goto err; + + found = nft_set_do_lookup(net, set, src, &ext); if (!found) { ext = nft_set_catchall_lookup(net, set); - if (!ext) { - regs->verdict.code = NFT_BREAK; - return; - } + if (!ext) + goto err; } obj = *nft_set_ext_obj(ext); obj->ops->eval(obj, regs, pkt); + + return; +err: + regs->verdict.code = NFT_BREAK; } static int nft_objref_map_init(const struct nft_ctx *ctx, diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c index 1c016089727b..20a6079667eb 100644 --- a/net/netfilter/nft_payload.c +++ b/net/netfilter/nft_payload.c @@ -805,9 +805,13 @@ static void nft_payload_set_eval(const struct nft_expr *expr, { const struct nft_payload_set *priv = nft_expr_priv(expr); struct sk_buff *skb = pkt->skb; - const u32 *src = ®s->data[priv->sreg]; int offset, csum_offset; __wsum fsum, tsum; + const u32 *src; + + src = nft_reg_load(regs, priv->sreg, priv->len); + if (!src) + goto err; switch (priv->base) { case NFT_PAYLOAD_LL_HEADER: diff --git a/net/netfilter/nft_queue.c b/net/netfilter/nft_queue.c index b2b8127c8d43..76fe8e97bdab 100644 --- a/net/netfilter/nft_queue.c +++ b/net/netfilter/nft_queue.c @@ -57,15 +57,22 @@ static void nft_queue_sreg_eval(const struct nft_expr *expr, const struct nft_pktinfo *pkt) { struct nft_queue *priv = nft_expr_priv(expr); - u32 queue, ret; + const u32 *queue; + u32 ret; - queue = regs->data[priv->sreg_qnum]; + queue = nft_reg_load_u32(regs, priv->sreg_qnum); + if (!queue) + goto err; - ret = NF_QUEUE_NR(queue); + ret = NF_QUEUE_NR(*queue); if (priv->flags & NFT_QUEUE_FLAG_BYPASS) ret |= NF_VERDICT_FLAG_QUEUE_BYPASS; regs->verdict.code = ret; + + return; +err: + regs->verdict.code = NFT_BREAK; } static int nft_queue_validate(const struct nft_ctx *ctx, diff --git a/net/netfilter/nft_range.c b/net/netfilter/nft_range.c index 0566d6aaf1e5..204a472bbafa 100644 --- a/net/netfilter/nft_range.c +++ b/net/netfilter/nft_range.c @@ -24,10 +24,15 @@ void nft_range_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) { const struct nft_range_expr *priv = nft_expr_priv(expr); + const u32 *src; int d1, d2; - d1 = memcmp(®s->data[priv->sreg], &priv->data_from, priv->len); - d2 = memcmp(®s->data[priv->sreg], &priv->data_to, priv->len); + src = nft_reg_load(regs, priv->sreg, priv->len); + if (!src) + goto err; + + d1 = memcmp(src, &priv->data_from, priv->len); + d2 = memcmp(src, &priv->data_to, priv->len); switch (priv->op) { case NFT_RANGE_EQ: if (d1 < 0 || d2 > 0) @@ -38,6 +43,10 @@ void nft_range_eval(const struct nft_expr *expr, regs->verdict.code = NFT_BREAK; break; } + + return; +err: + regs->verdict.code = NFT_BREAK; } static const struct nla_policy nft_range_policy[NFTA_RANGE_MAX + 1] = { diff --git a/net/netfilter/nft_redir.c b/net/netfilter/nft_redir.c index a70196ffcb1e..3c4a0ff450e3 100644 --- a/net/netfilter/nft_redir.c +++ b/net/netfilter/nft_redir.c @@ -106,15 +106,21 @@ static void nft_redir_eval(const struct nft_expr *expr, const struct nft_pktinfo *pkt) { const struct nft_redir *priv = nft_expr_priv(expr); + const __be16 *src_proto_min, *src_proto_max; struct nf_nat_range2 range; + if (priv->sreg_proto_min) { + src_proto_min = nft_reg_load_be16(regs, priv->sreg_proto_min); + src_proto_max = nft_reg_load_be16(regs, priv->sreg_proto_max); + if (!src_proto_min || !src_proto_max) + goto err; + } + memset(&range, 0, sizeof(range)); range.flags = priv->flags; if (priv->sreg_proto_min) { - range.min_proto.all = (__force __be16) - nft_reg_load16(®s->data[priv->sreg_proto_min]); - range.max_proto.all = (__force __be16) - nft_reg_load16(®s->data[priv->sreg_proto_max]); + range.min_proto.all = *src_proto_min; + range.max_proto.all = *src_proto_max; } switch (nft_pf(pkt)) { @@ -132,6 +138,10 @@ static void nft_redir_eval(const struct nft_expr *expr, WARN_ON_ONCE(1); break; } + + return; +err: + regs->verdict.code = NFT_BREAK; } static void diff --git a/net/netfilter/nft_tproxy.c b/net/netfilter/nft_tproxy.c index ea83f661417e..b2891237b9c1 100644 --- a/net/netfilter/nft_tproxy.c +++ b/net/netfilter/nft_tproxy.c @@ -26,6 +26,8 @@ static void nft_tproxy_eval_v4(const struct nft_expr *expr, struct sk_buff *skb = pkt->skb; const struct iphdr *iph = ip_hdr(skb); struct udphdr _hdr, *hp; + const __be32 *addr; + const __be16 *port; __be32 taddr = 0; __be16 tport = 0; struct sock *sk; @@ -51,12 +53,23 @@ static void nft_tproxy_eval_v4(const struct nft_expr *expr, hp->source, hp->dest, skb->dev, NF_TPROXY_LOOKUP_ESTABLISHED); - if (priv->sreg_addr) - taddr = nft_reg_load_be32(®s->data[priv->sreg_addr]); + if (priv->sreg_addr) { + addr = nft_reg_load_be32(regs, priv->sreg_addr); + if (!addr) + goto err; + + taddr = *addr; + } + taddr = nf_tproxy_laddr4(skb, taddr, iph->daddr); - if (priv->sreg_port) - tport = nft_reg_load_be16(®s->data[priv->sreg_port]); + if (priv->sreg_port) { + port = nft_reg_load_be16(regs, priv->sreg_port); + if (!port) + goto err; + + tport = *port; + } if (!tport) tport = hp->dest; @@ -78,6 +91,10 @@ static void nft_tproxy_eval_v4(const struct nft_expr *expr, nf_tproxy_assign_sock(skb, sk); else regs->verdict.code = NFT_BREAK; + + return; +err: + regs->verdict.code = NFT_BREAK; } #if IS_ENABLED(CONFIG_NF_TABLES_IPV6) @@ -92,6 +109,8 @@ static void nft_tproxy_eval_v6(const struct nft_expr *expr, struct udphdr _hdr, *hp; struct in6_addr taddr; __be16 tport = 0; + const u32 *addr; + const u16 *port; struct sock *sk; int l4proto; @@ -119,12 +138,22 @@ static void nft_tproxy_eval_v6(const struct nft_expr *expr, hp->source, hp->dest, nft_in(pkt), NF_TPROXY_LOOKUP_ESTABLISHED); - if (priv->sreg_addr) - memcpy(&taddr, ®s->data[priv->sreg_addr], sizeof(taddr)); + if (priv->sreg_addr) { + addr = nft_reg_load(regs, priv->sreg_addr, sizeof(taddr)); + if (!addr) + goto err; + + memcpy(&taddr, addr, sizeof(taddr)); + } taddr = *nf_tproxy_laddr6(skb, &taddr, &iph->daddr); - if (priv->sreg_port) - tport = nft_reg_load_be16(®s->data[priv->sreg_port]); + if (priv->sreg_port) { + port = nft_reg_load_be16(regs, priv->sreg_port); + if (!port) + goto err; + + tport = *port; + } if (!tport) tport = hp->dest; @@ -151,6 +180,10 @@ static void nft_tproxy_eval_v6(const struct nft_expr *expr, nf_tproxy_assign_sock(skb, sk); else regs->verdict.code = NFT_BREAK; + + return; +err: + regs->verdict.code = NFT_BREAK; } #endif -- 2.30.2