mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-05 18:41:58 +09:00
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:
@@ -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:
|
||||||
|
|||||||
@@ -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 */
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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]);
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user