Commit 55bc1af3 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf

Pablo Neira Ayuso says:

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

1) Add SECMARK revision 1 to fix incorrect layout that prevents
   from remove rule with this target, from Phil Sutter.

2) Fix pernet exit path spat in arptables, from Florian Westphal.

3) Missing rcu_read_unlock() for unknown nfnetlink callbacks,
   reported by syzbot, from Eric Dumazet.

4) Missing check for skb_header_pointer() NULL pointer in
   nfnetlink_osf.

5) Remove BUG_ON() after skb_header_pointer() from packet path
   in several conntrack helper and the TCP tracker.

6) Fix memleak in the new object error path of userdata.

7) Avoid overflows in nft_hash_buckets(), reported by syzbot,
   also from Eric.

8) Avoid overflows in 32bit arches, from Eric.

* git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf:
  netfilter: nftables: avoid potential overflows on 32bit arches
  netfilter: nftables: avoid overflows in nft_hash_buckets()
  netfilter: nftables: Fix a memleak from userdata error path in new objects
  netfilter: remove BUG_ON() after skb_header_pointer()
  netfilter: nfnetlink_osf: Fix a missing skb_header_pointer() NULL check
  netfilter: nfnetlink: add a missing rcu_read_unlock()
  netfilter: arptables: use pernet ops struct during unregister
  netfilter: xt_SECMARK: add new revision to fix structure layout
====================

Link: https://lore.kernel.org/r/20210507174739.1850-1-pablo@netfilter.orgSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents e4d4a272 6c8774a9
...@@ -53,8 +53,7 @@ int arpt_register_table(struct net *net, const struct xt_table *table, ...@@ -53,8 +53,7 @@ int arpt_register_table(struct net *net, const struct xt_table *table,
const struct arpt_replace *repl, const struct arpt_replace *repl,
const struct nf_hook_ops *ops); const struct nf_hook_ops *ops);
void arpt_unregister_table(struct net *net, const char *name); void arpt_unregister_table(struct net *net, const char *name);
void arpt_unregister_table_pre_exit(struct net *net, const char *name, void arpt_unregister_table_pre_exit(struct net *net, const char *name);
const struct nf_hook_ops *ops);
extern unsigned int arpt_do_table(struct sk_buff *skb, extern unsigned int arpt_do_table(struct sk_buff *skb,
const struct nf_hook_state *state, const struct nf_hook_state *state,
struct xt_table *table); struct xt_table *table);
......
...@@ -20,4 +20,10 @@ struct xt_secmark_target_info { ...@@ -20,4 +20,10 @@ struct xt_secmark_target_info {
char secctx[SECMARK_SECCTX_MAX]; char secctx[SECMARK_SECCTX_MAX];
}; };
struct xt_secmark_target_info_v1 {
__u8 mode;
char secctx[SECMARK_SECCTX_MAX];
__u32 secid;
};
#endif /*_XT_SECMARK_H_target */ #endif /*_XT_SECMARK_H_target */
...@@ -1556,13 +1556,12 @@ int arpt_register_table(struct net *net, ...@@ -1556,13 +1556,12 @@ int arpt_register_table(struct net *net,
return ret; return ret;
} }
void arpt_unregister_table_pre_exit(struct net *net, const char *name, void arpt_unregister_table_pre_exit(struct net *net, const char *name)
const struct nf_hook_ops *ops)
{ {
struct xt_table *table = xt_find_table(net, NFPROTO_ARP, name); struct xt_table *table = xt_find_table(net, NFPROTO_ARP, name);
if (table) if (table)
nf_unregister_net_hooks(net, ops, hweight32(table->valid_hooks)); nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks));
} }
EXPORT_SYMBOL(arpt_unregister_table_pre_exit); EXPORT_SYMBOL(arpt_unregister_table_pre_exit);
......
...@@ -54,7 +54,7 @@ static int __net_init arptable_filter_table_init(struct net *net) ...@@ -54,7 +54,7 @@ static int __net_init arptable_filter_table_init(struct net *net)
static void __net_exit arptable_filter_net_pre_exit(struct net *net) static void __net_exit arptable_filter_net_pre_exit(struct net *net)
{ {
arpt_unregister_table_pre_exit(net, "filter", arpfilter_ops); arpt_unregister_table_pre_exit(net, "filter");
} }
static void __net_exit arptable_filter_net_exit(struct net *net) static void __net_exit arptable_filter_net_exit(struct net *net)
......
...@@ -413,7 +413,10 @@ static int help(struct sk_buff *skb, ...@@ -413,7 +413,10 @@ static int help(struct sk_buff *skb,
spin_lock_bh(&nf_ftp_lock); spin_lock_bh(&nf_ftp_lock);
fb_ptr = skb_header_pointer(skb, dataoff, datalen, ftp_buffer); fb_ptr = skb_header_pointer(skb, dataoff, datalen, ftp_buffer);
BUG_ON(fb_ptr == NULL); if (!fb_ptr) {
spin_unlock_bh(&nf_ftp_lock);
return NF_ACCEPT;
}
ends_in_nl = (fb_ptr[datalen - 1] == '\n'); ends_in_nl = (fb_ptr[datalen - 1] == '\n');
seq = ntohl(th->seq) + datalen; seq = ntohl(th->seq) + datalen;
......
...@@ -146,7 +146,8 @@ static int get_tpkt_data(struct sk_buff *skb, unsigned int protoff, ...@@ -146,7 +146,8 @@ static int get_tpkt_data(struct sk_buff *skb, unsigned int protoff,
/* Get first TPKT pointer */ /* Get first TPKT pointer */
tpkt = skb_header_pointer(skb, tcpdataoff, tcpdatalen, tpkt = skb_header_pointer(skb, tcpdataoff, tcpdatalen,
h323_buffer); h323_buffer);
BUG_ON(tpkt == NULL); if (!tpkt)
goto clear_out;
/* Validate TPKT identifier */ /* Validate TPKT identifier */
if (tcpdatalen < 4 || tpkt[0] != 0x03 || tpkt[1] != 0) { if (tcpdatalen < 4 || tpkt[0] != 0x03 || tpkt[1] != 0) {
......
...@@ -143,7 +143,10 @@ static int help(struct sk_buff *skb, unsigned int protoff, ...@@ -143,7 +143,10 @@ static int help(struct sk_buff *skb, unsigned int protoff,
spin_lock_bh(&irc_buffer_lock); spin_lock_bh(&irc_buffer_lock);
ib_ptr = skb_header_pointer(skb, dataoff, skb->len - dataoff, ib_ptr = skb_header_pointer(skb, dataoff, skb->len - dataoff,
irc_buffer); irc_buffer);
BUG_ON(ib_ptr == NULL); if (!ib_ptr) {
spin_unlock_bh(&irc_buffer_lock);
return NF_ACCEPT;
}
data = ib_ptr; data = ib_ptr;
data_limit = ib_ptr + skb->len - dataoff; data_limit = ib_ptr + skb->len - dataoff;
......
...@@ -544,7 +544,9 @@ conntrack_pptp_help(struct sk_buff *skb, unsigned int protoff, ...@@ -544,7 +544,9 @@ conntrack_pptp_help(struct sk_buff *skb, unsigned int protoff,
nexthdr_off = protoff; nexthdr_off = protoff;
tcph = skb_header_pointer(skb, nexthdr_off, sizeof(_tcph), &_tcph); tcph = skb_header_pointer(skb, nexthdr_off, sizeof(_tcph), &_tcph);
BUG_ON(!tcph); if (!tcph)
return NF_ACCEPT;
nexthdr_off += tcph->doff * 4; nexthdr_off += tcph->doff * 4;
datalen = tcplen - tcph->doff * 4; datalen = tcplen - tcph->doff * 4;
......
...@@ -338,7 +338,8 @@ static void tcp_options(const struct sk_buff *skb, ...@@ -338,7 +338,8 @@ static void tcp_options(const struct sk_buff *skb,
ptr = skb_header_pointer(skb, dataoff + sizeof(struct tcphdr), ptr = skb_header_pointer(skb, dataoff + sizeof(struct tcphdr),
length, buff); length, buff);
BUG_ON(ptr == NULL); if (!ptr)
return;
state->td_scale = state->td_scale =
state->flags = 0; state->flags = 0;
...@@ -394,7 +395,8 @@ static void tcp_sack(const struct sk_buff *skb, unsigned int dataoff, ...@@ -394,7 +395,8 @@ static void tcp_sack(const struct sk_buff *skb, unsigned int dataoff,
ptr = skb_header_pointer(skb, dataoff + sizeof(struct tcphdr), ptr = skb_header_pointer(skb, dataoff + sizeof(struct tcphdr),
length, buff); length, buff);
BUG_ON(ptr == NULL); if (!ptr)
return;
/* Fast path for timestamp-only option */ /* Fast path for timestamp-only option */
if (length == TCPOLEN_TSTAMP_ALIGNED if (length == TCPOLEN_TSTAMP_ALIGNED
......
...@@ -95,7 +95,10 @@ static int help(struct sk_buff *skb, ...@@ -95,7 +95,10 @@ static int help(struct sk_buff *skb,
spin_lock_bh(&nf_sane_lock); spin_lock_bh(&nf_sane_lock);
sb_ptr = skb_header_pointer(skb, dataoff, datalen, sane_buffer); sb_ptr = skb_header_pointer(skb, dataoff, datalen, sane_buffer);
BUG_ON(sb_ptr == NULL); if (!sb_ptr) {
spin_unlock_bh(&nf_sane_lock);
return NF_ACCEPT;
}
if (dir == IP_CT_DIR_ORIGINAL) { if (dir == IP_CT_DIR_ORIGINAL) {
if (datalen != sizeof(struct sane_request)) if (datalen != sizeof(struct sane_request))
......
...@@ -4184,6 +4184,7 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info, ...@@ -4184,6 +4184,7 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
unsigned char *udata; unsigned char *udata;
struct nft_set *set; struct nft_set *set;
struct nft_ctx ctx; struct nft_ctx ctx;
size_t alloc_size;
u64 timeout; u64 timeout;
char *name; char *name;
int err, i; int err, i;
...@@ -4329,8 +4330,10 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info, ...@@ -4329,8 +4330,10 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
size = 0; size = 0;
if (ops->privsize != NULL) if (ops->privsize != NULL)
size = ops->privsize(nla, &desc); size = ops->privsize(nla, &desc);
alloc_size = sizeof(*set) + size + udlen;
set = kvzalloc(sizeof(*set) + size + udlen, GFP_KERNEL); if (alloc_size < size)
return -ENOMEM;
set = kvzalloc(alloc_size, GFP_KERNEL);
if (!set) if (!set)
return -ENOMEM; return -ENOMEM;
...@@ -6615,9 +6618,9 @@ static int nf_tables_newobj(struct sk_buff *skb, const struct nfnl_info *info, ...@@ -6615,9 +6618,9 @@ static int nf_tables_newobj(struct sk_buff *skb, const struct nfnl_info *info,
INIT_LIST_HEAD(&obj->list); INIT_LIST_HEAD(&obj->list);
return err; return err;
err_trans: err_trans:
kfree(obj->key.name);
err_userdata:
kfree(obj->udata); kfree(obj->udata);
err_userdata:
kfree(obj->key.name);
err_strdup: err_strdup:
if (obj->ops->destroy) if (obj->ops->destroy)
obj->ops->destroy(&ctx, obj); obj->ops->destroy(&ctx, obj);
......
...@@ -295,6 +295,7 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -295,6 +295,7 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
nfnl_unlock(subsys_id); nfnl_unlock(subsys_id);
break; break;
default: default:
rcu_read_unlock();
err = -EINVAL; err = -EINVAL;
break; break;
} }
......
...@@ -186,6 +186,8 @@ static const struct tcphdr *nf_osf_hdr_ctx_init(struct nf_osf_hdr_ctx *ctx, ...@@ -186,6 +186,8 @@ static const struct tcphdr *nf_osf_hdr_ctx_init(struct nf_osf_hdr_ctx *ctx,
ctx->optp = skb_header_pointer(skb, ip_hdrlen(skb) + ctx->optp = skb_header_pointer(skb, ip_hdrlen(skb) +
sizeof(struct tcphdr), ctx->optsize, opts); sizeof(struct tcphdr), ctx->optsize, opts);
if (!ctx->optp)
return NULL;
} }
return tcp; return tcp;
......
...@@ -412,9 +412,17 @@ static void nft_rhash_destroy(const struct nft_set *set) ...@@ -412,9 +412,17 @@ static void nft_rhash_destroy(const struct nft_set *set)
(void *)set); (void *)set);
} }
/* Number of buckets is stored in u32, so cap our result to 1U<<31 */
#define NFT_MAX_BUCKETS (1U << 31)
static u32 nft_hash_buckets(u32 size) static u32 nft_hash_buckets(u32 size)
{ {
return roundup_pow_of_two(size * 4 / 3); u64 val = div_u64((u64)size * 4, 3);
if (val >= NFT_MAX_BUCKETS)
return NFT_MAX_BUCKETS;
return roundup_pow_of_two(val);
} }
static bool nft_rhash_estimate(const struct nft_set_desc *desc, u32 features, static bool nft_rhash_estimate(const struct nft_set_desc *desc, u32 features,
...@@ -615,7 +623,7 @@ static u64 nft_hash_privsize(const struct nlattr * const nla[], ...@@ -615,7 +623,7 @@ static u64 nft_hash_privsize(const struct nlattr * const nla[],
const struct nft_set_desc *desc) const struct nft_set_desc *desc)
{ {
return sizeof(struct nft_hash) + return sizeof(struct nft_hash) +
nft_hash_buckets(desc->size) * sizeof(struct hlist_head); (u64)nft_hash_buckets(desc->size) * sizeof(struct hlist_head);
} }
static int nft_hash_init(const struct nft_set *set, static int nft_hash_init(const struct nft_set *set,
...@@ -655,8 +663,8 @@ static bool nft_hash_estimate(const struct nft_set_desc *desc, u32 features, ...@@ -655,8 +663,8 @@ static bool nft_hash_estimate(const struct nft_set_desc *desc, u32 features,
return false; return false;
est->size = sizeof(struct nft_hash) + est->size = sizeof(struct nft_hash) +
nft_hash_buckets(desc->size) * sizeof(struct hlist_head) + (u64)nft_hash_buckets(desc->size) * sizeof(struct hlist_head) +
desc->size * sizeof(struct nft_hash_elem); (u64)desc->size * sizeof(struct nft_hash_elem);
est->lookup = NFT_SET_CLASS_O_1; est->lookup = NFT_SET_CLASS_O_1;
est->space = NFT_SET_CLASS_O_N; est->space = NFT_SET_CLASS_O_N;
...@@ -673,8 +681,8 @@ static bool nft_hash_fast_estimate(const struct nft_set_desc *desc, u32 features ...@@ -673,8 +681,8 @@ static bool nft_hash_fast_estimate(const struct nft_set_desc *desc, u32 features
return false; return false;
est->size = sizeof(struct nft_hash) + est->size = sizeof(struct nft_hash) +
nft_hash_buckets(desc->size) * sizeof(struct hlist_head) + (u64)nft_hash_buckets(desc->size) * sizeof(struct hlist_head) +
desc->size * sizeof(struct nft_hash_elem); (u64)desc->size * sizeof(struct nft_hash_elem);
est->lookup = NFT_SET_CLASS_O_1; est->lookup = NFT_SET_CLASS_O_1;
est->space = NFT_SET_CLASS_O_N; est->space = NFT_SET_CLASS_O_N;
......
...@@ -24,10 +24,9 @@ MODULE_ALIAS("ip6t_SECMARK"); ...@@ -24,10 +24,9 @@ MODULE_ALIAS("ip6t_SECMARK");
static u8 mode; static u8 mode;
static unsigned int static unsigned int
secmark_tg(struct sk_buff *skb, const struct xt_action_param *par) secmark_tg(struct sk_buff *skb, const struct xt_secmark_target_info_v1 *info)
{ {
u32 secmark = 0; u32 secmark = 0;
const struct xt_secmark_target_info *info = par->targinfo;
switch (mode) { switch (mode) {
case SECMARK_MODE_SEL: case SECMARK_MODE_SEL:
...@@ -41,7 +40,7 @@ secmark_tg(struct sk_buff *skb, const struct xt_action_param *par) ...@@ -41,7 +40,7 @@ secmark_tg(struct sk_buff *skb, const struct xt_action_param *par)
return XT_CONTINUE; return XT_CONTINUE;
} }
static int checkentry_lsm(struct xt_secmark_target_info *info) static int checkentry_lsm(struct xt_secmark_target_info_v1 *info)
{ {
int err; int err;
...@@ -73,15 +72,15 @@ static int checkentry_lsm(struct xt_secmark_target_info *info) ...@@ -73,15 +72,15 @@ static int checkentry_lsm(struct xt_secmark_target_info *info)
return 0; return 0;
} }
static int secmark_tg_check(const struct xt_tgchk_param *par) static int
secmark_tg_check(const char *table, struct xt_secmark_target_info_v1 *info)
{ {
struct xt_secmark_target_info *info = par->targinfo;
int err; int err;
if (strcmp(par->table, "mangle") != 0 && if (strcmp(table, "mangle") != 0 &&
strcmp(par->table, "security") != 0) { strcmp(table, "security") != 0) {
pr_info_ratelimited("only valid in \'mangle\' or \'security\' table, not \'%s\'\n", pr_info_ratelimited("only valid in \'mangle\' or \'security\' table, not \'%s\'\n",
par->table); table);
return -EINVAL; return -EINVAL;
} }
...@@ -116,25 +115,76 @@ static void secmark_tg_destroy(const struct xt_tgdtor_param *par) ...@@ -116,25 +115,76 @@ static void secmark_tg_destroy(const struct xt_tgdtor_param *par)
} }
} }
static struct xt_target secmark_tg_reg __read_mostly = { static int secmark_tg_check_v0(const struct xt_tgchk_param *par)
.name = "SECMARK", {
.revision = 0, struct xt_secmark_target_info *info = par->targinfo;
.family = NFPROTO_UNSPEC, struct xt_secmark_target_info_v1 newinfo = {
.checkentry = secmark_tg_check, .mode = info->mode,
.destroy = secmark_tg_destroy, };
.target = secmark_tg, int ret;
.targetsize = sizeof(struct xt_secmark_target_info),
.me = THIS_MODULE, memcpy(newinfo.secctx, info->secctx, SECMARK_SECCTX_MAX);
ret = secmark_tg_check(par->table, &newinfo);
info->secid = newinfo.secid;
return ret;
}
static unsigned int
secmark_tg_v0(struct sk_buff *skb, const struct xt_action_param *par)
{
const struct xt_secmark_target_info *info = par->targinfo;
struct xt_secmark_target_info_v1 newinfo = {
.secid = info->secid,
};
return secmark_tg(skb, &newinfo);
}
static int secmark_tg_check_v1(const struct xt_tgchk_param *par)
{
return secmark_tg_check(par->table, par->targinfo);
}
static unsigned int
secmark_tg_v1(struct sk_buff *skb, const struct xt_action_param *par)
{
return secmark_tg(skb, par->targinfo);
}
static struct xt_target secmark_tg_reg[] __read_mostly = {
{
.name = "SECMARK",
.revision = 0,
.family = NFPROTO_UNSPEC,
.checkentry = secmark_tg_check_v0,
.destroy = secmark_tg_destroy,
.target = secmark_tg_v0,
.targetsize = sizeof(struct xt_secmark_target_info),
.me = THIS_MODULE,
},
{
.name = "SECMARK",
.revision = 1,
.family = NFPROTO_UNSPEC,
.checkentry = secmark_tg_check_v1,
.destroy = secmark_tg_destroy,
.target = secmark_tg_v1,
.targetsize = sizeof(struct xt_secmark_target_info_v1),
.usersize = offsetof(struct xt_secmark_target_info_v1, secid),
.me = THIS_MODULE,
},
}; };
static int __init secmark_tg_init(void) static int __init secmark_tg_init(void)
{ {
return xt_register_target(&secmark_tg_reg); return xt_register_targets(secmark_tg_reg, ARRAY_SIZE(secmark_tg_reg));
} }
static void __exit secmark_tg_exit(void) static void __exit secmark_tg_exit(void)
{ {
xt_unregister_target(&secmark_tg_reg); xt_unregister_targets(secmark_tg_reg, ARRAY_SIZE(secmark_tg_reg));
} }
module_init(secmark_tg_init); module_init(secmark_tg_init);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment