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; break;
case NFPROTO_BRIDGE: case NFPROTO_BRIDGE:
#ifdef CONFIG_NETFILTER_FAMILY_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 #endif
break; break;
default: default:

View File

@@ -188,6 +188,36 @@ struct net {
#endif #endif
} __randomize_layout; } __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> #include <linux/seq_file_net.h>
/* Init's network namespace */ /* Init's network namespace */

View File

@@ -22,9 +22,6 @@ struct netns_nf {
#ifdef CONFIG_NETFILTER_FAMILY_ARP #ifdef CONFIG_NETFILTER_FAMILY_ARP
struct nf_hook_entries __rcu *hooks_arp[NF_ARP_NUMHOOKS]; struct nf_hook_entries __rcu *hooks_arp[NF_ARP_NUMHOOKS];
#endif #endif
#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
struct nf_hook_entries __rcu *hooks_bridge[NF_INET_NUMHOOKS];
#endif
#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4) #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4)
unsigned int defrag_ipv4_users; unsigned int defrag_ipv4_users;
#endif #endif

View File

@@ -202,6 +202,7 @@ config GKI_HIDDEN_NET_CONFIGS
select PAGE_POOL select PAGE_POOL
select NET_PTP_CLASSIFY select NET_PTP_CLASSIFY
select NET_DEVLINK select NET_DEVLINK
select NETFILTER_FAMILY_BRIDGE
help help
Dummy config option used to enable the networking hidden Dummy config option used to enable the networking hidden
config, required by various SoC platforms. 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; goto frame_finish;
#endif #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) if (!e)
goto frame_finish; goto frame_finish;

View File

@@ -1016,7 +1016,7 @@ int br_nf_hook_thresh(unsigned int hook, struct net *net,
unsigned int i; unsigned int i;
int ret; int ret;
e = rcu_dereference(net->nf.hooks_bridge[hook]); e = rcu_dereference(get_nf_hooks_bridge(net)[hook]);
if (!e) if (!e)
return okfn(net, sk, skb); return okfn(net, sk, skb);

View File

@@ -1093,9 +1093,13 @@ void __init net_ns_init(void)
struct net_generic *ng; struct net_generic *ng;
#ifdef CONFIG_NET_NS #ifdef CONFIG_NET_NS
net_cachep = kmem_cache_create("net_namespace", sizeof(struct net), /* Allocate size for struct ext_net instead of struct net
SMP_CACHE_BYTES, * to fix a KMI issue when CONFIG_NETFILTER_FAMILY_BRIDGE
SLAB_PANIC|SLAB_ACCOUNT, NULL); * 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 */ /* Create workqueue for cleanup */
netns_wq = create_singlethread_workqueue("netns"); 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); EXPORT_SYMBOL(nf_hooks_needed);
#endif #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); static DEFINE_MUTEX(nf_hook_mutex);
/* max hooks per family/hooknum */ /* max hooks per family/hooknum */
@@ -278,9 +284,9 @@ nf_hook_entry_head(struct net *net, int pf, unsigned int hooknum,
#endif #endif
#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE #ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
case NFPROTO_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 NULL;
return net->nf.hooks_bridge + hooknum; return get_nf_hooks_bridge(net) + hooknum;
#endif #endif
#ifdef CONFIG_NETFILTER_INGRESS #ifdef CONFIG_NETFILTER_INGRESS
case NFPROTO_INET: 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)); __netfilter_net_init(net->nf.hooks_arp, ARRAY_SIZE(net->nf.hooks_arp));
#endif #endif
#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE #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 #endif
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
net->nf.proc_netfilter = proc_net_mkdir(net, "netfilter", 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) { switch (pf) {
#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE #ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
case NFPROTO_BRIDGE: case NFPROTO_BRIDGE:
return rcu_dereference(net->nf.hooks_bridge[hooknum]); return rcu_dereference(get_nf_hooks_bridge(net)[hooknum]);
#endif #endif
case NFPROTO_IPV4: case NFPROTO_IPV4:
return rcu_dereference(net->nf.hooks_ipv4[hooknum]); 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; break;
case NFPROTO_BRIDGE: case NFPROTO_BRIDGE:
#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE #ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
if (hook >= ARRAY_SIZE(net->nf.hooks_bridge)) if (hook >= NF_INET_NUMHOOKS)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
hook_head = rcu_dereference(net->nf.hooks_bridge[hook]); hook_head = rcu_dereference(get_nf_hooks_bridge(net)[hook]);
#endif #endif
break; break;
#if defined(CONFIG_NETFILTER_INGRESS) || defined(CONFIG_NETFILTER_EGRESS) #if defined(CONFIG_NETFILTER_INGRESS) || defined(CONFIG_NETFILTER_EGRESS)