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.org Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Коммит
55bc1af3d9
|
@ -53,8 +53,7 @@ int arpt_register_table(struct net *net, const struct xt_table *table,
|
|||
const struct arpt_replace *repl,
|
||||
const struct nf_hook_ops *ops);
|
||||
void arpt_unregister_table(struct net *net, const char *name);
|
||||
void arpt_unregister_table_pre_exit(struct net *net, const char *name,
|
||||
const struct nf_hook_ops *ops);
|
||||
void arpt_unregister_table_pre_exit(struct net *net, const char *name);
|
||||
extern unsigned int arpt_do_table(struct sk_buff *skb,
|
||||
const struct nf_hook_state *state,
|
||||
struct xt_table *table);
|
||||
|
|
|
@ -20,4 +20,10 @@ struct xt_secmark_target_info {
|
|||
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 */
|
||||
|
|
|
@ -1556,13 +1556,12 @@ out_free:
|
|||
return ret;
|
||||
}
|
||||
|
||||
void arpt_unregister_table_pre_exit(struct net *net, const char *name,
|
||||
const struct nf_hook_ops *ops)
|
||||
void arpt_unregister_table_pre_exit(struct net *net, const char *name)
|
||||
{
|
||||
struct xt_table *table = xt_find_table(net, NFPROTO_ARP, name);
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
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)
|
||||
|
|
|
@ -413,7 +413,10 @@ static int help(struct sk_buff *skb,
|
|||
|
||||
spin_lock_bh(&nf_ftp_lock);
|
||||
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');
|
||||
seq = ntohl(th->seq) + datalen;
|
||||
|
|
|
@ -146,7 +146,8 @@ static int get_tpkt_data(struct sk_buff *skb, unsigned int protoff,
|
|||
/* Get first TPKT pointer */
|
||||
tpkt = skb_header_pointer(skb, tcpdataoff, tcpdatalen,
|
||||
h323_buffer);
|
||||
BUG_ON(tpkt == NULL);
|
||||
if (!tpkt)
|
||||
goto clear_out;
|
||||
|
||||
/* Validate TPKT identifier */
|
||||
if (tcpdatalen < 4 || tpkt[0] != 0x03 || tpkt[1] != 0) {
|
||||
|
|
|
@ -143,7 +143,10 @@ static int help(struct sk_buff *skb, unsigned int protoff,
|
|||
spin_lock_bh(&irc_buffer_lock);
|
||||
ib_ptr = skb_header_pointer(skb, dataoff, skb->len - dataoff,
|
||||
irc_buffer);
|
||||
BUG_ON(ib_ptr == NULL);
|
||||
if (!ib_ptr) {
|
||||
spin_unlock_bh(&irc_buffer_lock);
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
|
||||
data = ib_ptr;
|
||||
data_limit = ib_ptr + skb->len - dataoff;
|
||||
|
|
|
@ -544,7 +544,9 @@ conntrack_pptp_help(struct sk_buff *skb, unsigned int protoff,
|
|||
|
||||
nexthdr_off = protoff;
|
||||
tcph = skb_header_pointer(skb, nexthdr_off, sizeof(_tcph), &_tcph);
|
||||
BUG_ON(!tcph);
|
||||
if (!tcph)
|
||||
return NF_ACCEPT;
|
||||
|
||||
nexthdr_off += tcph->doff * 4;
|
||||
datalen = tcplen - tcph->doff * 4;
|
||||
|
||||
|
|
|
@ -338,7 +338,8 @@ static void tcp_options(const struct sk_buff *skb,
|
|||
|
||||
ptr = skb_header_pointer(skb, dataoff + sizeof(struct tcphdr),
|
||||
length, buff);
|
||||
BUG_ON(ptr == NULL);
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
state->td_scale =
|
||||
state->flags = 0;
|
||||
|
@ -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),
|
||||
length, buff);
|
||||
BUG_ON(ptr == NULL);
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
/* Fast path for timestamp-only option */
|
||||
if (length == TCPOLEN_TSTAMP_ALIGNED
|
||||
|
|
|
@ -95,7 +95,10 @@ static int help(struct sk_buff *skb,
|
|||
|
||||
spin_lock_bh(&nf_sane_lock);
|
||||
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 (datalen != sizeof(struct sane_request))
|
||||
|
|
|
@ -4184,6 +4184,7 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
|
|||
unsigned char *udata;
|
||||
struct nft_set *set;
|
||||
struct nft_ctx ctx;
|
||||
size_t alloc_size;
|
||||
u64 timeout;
|
||||
char *name;
|
||||
int err, i;
|
||||
|
@ -4329,8 +4330,10 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
|
|||
size = 0;
|
||||
if (ops->privsize != NULL)
|
||||
size = ops->privsize(nla, &desc);
|
||||
|
||||
set = kvzalloc(sizeof(*set) + size + udlen, GFP_KERNEL);
|
||||
alloc_size = sizeof(*set) + size + udlen;
|
||||
if (alloc_size < size)
|
||||
return -ENOMEM;
|
||||
set = kvzalloc(alloc_size, GFP_KERNEL);
|
||||
if (!set)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -6615,9 +6618,9 @@ err_obj_ht:
|
|||
INIT_LIST_HEAD(&obj->list);
|
||||
return err;
|
||||
err_trans:
|
||||
kfree(obj->key.name);
|
||||
err_userdata:
|
||||
kfree(obj->udata);
|
||||
err_userdata:
|
||||
kfree(obj->key.name);
|
||||
err_strdup:
|
||||
if (obj->ops->destroy)
|
||||
obj->ops->destroy(&ctx, obj);
|
||||
|
|
|
@ -295,6 +295,7 @@ replay:
|
|||
nfnl_unlock(subsys_id);
|
||||
break;
|
||||
default:
|
||||
rcu_read_unlock();
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -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) +
|
||||
sizeof(struct tcphdr), ctx->optsize, opts);
|
||||
if (!ctx->optp)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return tcp;
|
||||
|
|
|
@ -412,9 +412,17 @@ static void nft_rhash_destroy(const struct nft_set *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)
|
||||
{
|
||||
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,
|
||||
|
@ -615,7 +623,7 @@ static u64 nft_hash_privsize(const struct nlattr * const nla[],
|
|||
const struct nft_set_desc *desc)
|
||||
{
|
||||
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,
|
||||
|
@ -655,8 +663,8 @@ static bool nft_hash_estimate(const struct nft_set_desc *desc, u32 features,
|
|||
return false;
|
||||
|
||||
est->size = sizeof(struct nft_hash) +
|
||||
nft_hash_buckets(desc->size) * sizeof(struct hlist_head) +
|
||||
desc->size * sizeof(struct nft_hash_elem);
|
||||
(u64)nft_hash_buckets(desc->size) * sizeof(struct hlist_head) +
|
||||
(u64)desc->size * sizeof(struct nft_hash_elem);
|
||||
est->lookup = NFT_SET_CLASS_O_1;
|
||||
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
|
|||
return false;
|
||||
|
||||
est->size = sizeof(struct nft_hash) +
|
||||
nft_hash_buckets(desc->size) * sizeof(struct hlist_head) +
|
||||
desc->size * sizeof(struct nft_hash_elem);
|
||||
(u64)nft_hash_buckets(desc->size) * sizeof(struct hlist_head) +
|
||||
(u64)desc->size * sizeof(struct nft_hash_elem);
|
||||
est->lookup = NFT_SET_CLASS_O_1;
|
||||
est->space = NFT_SET_CLASS_O_N;
|
||||
|
||||
|
|
|
@ -24,10 +24,9 @@ MODULE_ALIAS("ip6t_SECMARK");
|
|||
static u8 mode;
|
||||
|
||||
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;
|
||||
const struct xt_secmark_target_info *info = par->targinfo;
|
||||
|
||||
switch (mode) {
|
||||
case SECMARK_MODE_SEL:
|
||||
|
@ -41,7 +40,7 @@ secmark_tg(struct sk_buff *skb, const struct xt_action_param *par)
|
|||
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;
|
||||
|
||||
|
@ -73,15 +72,15 @@ static int checkentry_lsm(struct xt_secmark_target_info *info)
|
|||
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;
|
||||
|
||||
if (strcmp(par->table, "mangle") != 0 &&
|
||||
strcmp(par->table, "security") != 0) {
|
||||
if (strcmp(table, "mangle") != 0 &&
|
||||
strcmp(table, "security") != 0) {
|
||||
pr_info_ratelimited("only valid in \'mangle\' or \'security\' table, not \'%s\'\n",
|
||||
par->table);
|
||||
table);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -116,25 +115,76 @@ static void secmark_tg_destroy(const struct xt_tgdtor_param *par)
|
|||
}
|
||||
}
|
||||
|
||||
static struct xt_target secmark_tg_reg __read_mostly = {
|
||||
.name = "SECMARK",
|
||||
.revision = 0,
|
||||
.family = NFPROTO_UNSPEC,
|
||||
.checkentry = secmark_tg_check,
|
||||
.destroy = secmark_tg_destroy,
|
||||
.target = secmark_tg,
|
||||
.targetsize = sizeof(struct xt_secmark_target_info),
|
||||
.me = THIS_MODULE,
|
||||
static int secmark_tg_check_v0(const struct xt_tgchk_param *par)
|
||||
{
|
||||
struct xt_secmark_target_info *info = par->targinfo;
|
||||
struct xt_secmark_target_info_v1 newinfo = {
|
||||
.mode = info->mode,
|
||||
};
|
||||
int ret;
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
xt_unregister_target(&secmark_tg_reg);
|
||||
xt_unregister_targets(secmark_tg_reg, ARRAY_SIZE(secmark_tg_reg));
|
||||
}
|
||||
|
||||
module_init(secmark_tg_init);
|
||||
|
|
Загрузка…
Ссылка в новой задаче