ANDROID: KMI workaround for CONFIG_NETFILTER_FAMILY_BRIDGE
Enabling CONFIG_NETFILTER_FAMILY_BRIDGE causes the new element, hooks_bridge[] to be added to netns_nf. Since the KMI is frozen this could not be added. The only instantiation of struct netns_nf is as an embedded field of struct net. So instead of adding the field to struct netns_nf, a new "struct ext_net" is added that contains struct net and the new hooks_bridge[] field. An accessor function, get_nf_hooks_bridge() is added to get a pointer to the new field. There is a global init_net of type struct net which must be special cased since it is not a member of a struct ext_net. All other instances of struct net are allocated via net_alloc() which now allocates a struct ext_net. Since CONFIG_NETFILTER_FAMILY_BRIDGE is a hidden config that is needed for vendor modules, it is enabled via init/Kconfig.gki. Bug: 316040984 Fixes: 0145780bfc78 ("fix KASAN-related kernel crash by KMI W/A for NETFILTER_FAMILY_BRIDGE") Change-Id: I2c7384e3df9b88f12464dc0138986fed12ca626a Signed-off-by: Norihiko Hama <Norihiko.Hama@alpsalpine.com>
This commit is contained in:
parent
227b55a7a3
commit
bc4d82ee40
@ -243,7 +243,7 @@ static inline int nf_hook(u_int8_t pf, unsigned int hook, struct net *net,
|
||||
break;
|
||||
case NFPROTO_BRIDGE:
|
||||
#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
|
||||
hook_head = rcu_dereference(net->nf.hooks_bridge[hook]);
|
||||
hook_head = rcu_dereference(get_nf_hooks_bridge(net)[hook]);
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
|
@ -188,6 +188,36 @@ struct net {
|
||||
#endif
|
||||
} __randomize_layout;
|
||||
|
||||
/*
|
||||
* To work around a KMI issue, hooks_bridge[] could not be
|
||||
* added to struct netns_nf. Since the only use of netns_nf
|
||||
* is embedded in struct net, struct ext_net is added to
|
||||
* contain struct net plus the new field. Users of the new
|
||||
* field must use get_nf_hooks_bridge() to access the field.
|
||||
*/
|
||||
struct ext_net {
|
||||
struct net net;
|
||||
#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
|
||||
struct nf_hook_entries __rcu *hooks_bridge[NF_INET_NUMHOOKS];
|
||||
#endif
|
||||
ANDROID_VENDOR_DATA(1);
|
||||
};
|
||||
|
||||
#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
|
||||
extern struct net init_net;
|
||||
extern struct nf_hook_entries **init_nf_hooks_bridgep;
|
||||
|
||||
static inline struct nf_hook_entries __rcu **get_nf_hooks_bridge(const struct net *net)
|
||||
{
|
||||
struct ext_net *ext_net;
|
||||
|
||||
if (net == &init_net)
|
||||
return init_nf_hooks_bridgep;
|
||||
ext_net = container_of(net, struct ext_net, net);
|
||||
return ext_net->hooks_bridge;
|
||||
}
|
||||
#endif
|
||||
|
||||
#include <linux/seq_file_net.h>
|
||||
|
||||
/* Init's network namespace */
|
||||
|
@ -22,9 +22,6 @@ struct netns_nf {
|
||||
#ifdef CONFIG_NETFILTER_FAMILY_ARP
|
||||
struct nf_hook_entries __rcu *hooks_arp[NF_ARP_NUMHOOKS];
|
||||
#endif
|
||||
#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
|
||||
struct nf_hook_entries __rcu *hooks_bridge[NF_INET_NUMHOOKS];
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4)
|
||||
unsigned int defrag_ipv4_users;
|
||||
#endif
|
||||
|
@ -202,6 +202,7 @@ config GKI_HIDDEN_NET_CONFIGS
|
||||
select PAGE_POOL
|
||||
select NET_PTP_CLASSIFY
|
||||
select NET_DEVLINK
|
||||
select NETFILTER_FAMILY_BRIDGE
|
||||
help
|
||||
Dummy config option used to enable the networking hidden
|
||||
config, required by various SoC platforms.
|
||||
|
@ -243,7 +243,7 @@ static int nf_hook_bridge_pre(struct sk_buff *skb, struct sk_buff **pskb)
|
||||
goto frame_finish;
|
||||
#endif
|
||||
|
||||
e = rcu_dereference(net->nf.hooks_bridge[NF_BR_PRE_ROUTING]);
|
||||
e = rcu_dereference(get_nf_hooks_bridge(net)[NF_BR_PRE_ROUTING]);
|
||||
if (!e)
|
||||
goto frame_finish;
|
||||
|
||||
|
@ -1016,7 +1016,7 @@ int br_nf_hook_thresh(unsigned int hook, struct net *net,
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
e = rcu_dereference(net->nf.hooks_bridge[hook]);
|
||||
e = rcu_dereference(get_nf_hooks_bridge(net)[hook]);
|
||||
if (!e)
|
||||
return okfn(net, sk, skb);
|
||||
|
||||
|
@ -1093,9 +1093,13 @@ void __init net_ns_init(void)
|
||||
struct net_generic *ng;
|
||||
|
||||
#ifdef CONFIG_NET_NS
|
||||
net_cachep = kmem_cache_create("net_namespace", sizeof(struct net),
|
||||
SMP_CACHE_BYTES,
|
||||
SLAB_PANIC|SLAB_ACCOUNT, NULL);
|
||||
/* Allocate size for struct ext_net instead of struct net
|
||||
* to fix a KMI issue when CONFIG_NETFILTER_FAMILY_BRIDGE
|
||||
* is enabled
|
||||
*/
|
||||
net_cachep = kmem_cache_create("net_namespace", sizeof(struct ext_net),
|
||||
SMP_CACHE_BYTES,
|
||||
SLAB_PANIC | SLAB_ACCOUNT, NULL);
|
||||
|
||||
/* Create workqueue for cleanup */
|
||||
netns_wq = create_singlethread_workqueue("netns");
|
||||
|
@ -39,6 +39,12 @@ struct static_key nf_hooks_needed[NFPROTO_NUMPROTO][NF_MAX_HOOKS];
|
||||
EXPORT_SYMBOL(nf_hooks_needed);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
|
||||
struct nf_hook_entries __rcu *init_nf_hooks_bridge[NF_INET_NUMHOOKS];
|
||||
struct nf_hook_entries __rcu **init_nf_hooks_bridgep = &init_nf_hooks_bridge[0];
|
||||
EXPORT_SYMBOL_GPL(init_nf_hooks_bridgep);
|
||||
#endif
|
||||
|
||||
static DEFINE_MUTEX(nf_hook_mutex);
|
||||
|
||||
/* max hooks per family/hooknum */
|
||||
@ -278,9 +284,9 @@ nf_hook_entry_head(struct net *net, int pf, unsigned int hooknum,
|
||||
#endif
|
||||
#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
|
||||
case NFPROTO_BRIDGE:
|
||||
if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_bridge) <= hooknum))
|
||||
if (WARN_ON_ONCE(hooknum >= NF_INET_NUMHOOKS))
|
||||
return NULL;
|
||||
return net->nf.hooks_bridge + hooknum;
|
||||
return get_nf_hooks_bridge(net) + hooknum;
|
||||
#endif
|
||||
#ifdef CONFIG_NETFILTER_INGRESS
|
||||
case NFPROTO_INET:
|
||||
@ -747,7 +753,7 @@ static int __net_init netfilter_net_init(struct net *net)
|
||||
__netfilter_net_init(net->nf.hooks_arp, ARRAY_SIZE(net->nf.hooks_arp));
|
||||
#endif
|
||||
#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
|
||||
__netfilter_net_init(net->nf.hooks_bridge, ARRAY_SIZE(net->nf.hooks_bridge));
|
||||
__netfilter_net_init(get_nf_hooks_bridge(net), NF_INET_NUMHOOKS);
|
||||
#endif
|
||||
#ifdef CONFIG_PROC_FS
|
||||
net->nf.proc_netfilter = proc_net_mkdir(net, "netfilter",
|
||||
|
@ -281,7 +281,7 @@ static struct nf_hook_entries *nf_hook_entries_head(const struct net *net, u8 pf
|
||||
switch (pf) {
|
||||
#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
|
||||
case NFPROTO_BRIDGE:
|
||||
return rcu_dereference(net->nf.hooks_bridge[hooknum]);
|
||||
return rcu_dereference(get_nf_hooks_bridge(net)[hooknum]);
|
||||
#endif
|
||||
case NFPROTO_IPV4:
|
||||
return rcu_dereference(net->nf.hooks_ipv4[hooknum]);
|
||||
|
@ -210,9 +210,9 @@ nfnl_hook_entries_head(u8 pf, unsigned int hook, struct net *net, const char *de
|
||||
break;
|
||||
case NFPROTO_BRIDGE:
|
||||
#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
|
||||
if (hook >= ARRAY_SIZE(net->nf.hooks_bridge))
|
||||
if (hook >= NF_INET_NUMHOOKS)
|
||||
return ERR_PTR(-EINVAL);
|
||||
hook_head = rcu_dereference(net->nf.hooks_bridge[hook]);
|
||||
hook_head = rcu_dereference(get_nf_hooks_bridge(net)[hook]);
|
||||
#endif
|
||||
break;
|
||||
#if defined(CONFIG_NETFILTER_INGRESS) || defined(CONFIG_NETFILTER_EGRESS)
|
||||
|
Loading…
Reference in New Issue
Block a user