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:
Norihiko Hama 2023-12-15 12:04:47 +09:00 committed by Todd Kjos
parent 227b55a7a3
commit bc4d82ee40
10 changed files with 53 additions and 15 deletions

View File

@ -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:

View File

@ -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 */

View File

@ -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

View File

@ -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.

View File

@ -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;

View File

@ -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);

View File

@ -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");

View File

@ -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",

View File

@ -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]);

View File

@ -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)