[PATCH] net: caif: Don't act on notification for non-caif devices

Subsystems: networking [general], the rest

STALE5310d

5 messages, 2 authors, 2012-01-24 · open the first message on its own page

[PATCH] net: caif: Don't act on notification for non-caif devices

From: Sasha Levin <hidden>
Date: 2012-01-24 05:31:42

Currently we assume every notification happens within a network namespace
in which CAIF was already initialized. This is not true when we're copying
the namespace and the notifier is being called before the initialization
code runs.

Since the list of CAIF devices is stored in the net generic struct in each
net namespace, which is not initialized at that point, we see the following
BUG():

[  200.752016] kernel BUG at include/net/netns/generic.h:40!
[  200.752016] invalid opcode: 0000 [#1] PREEMPT SMP
[  200.752016] CPU 0
[  200.752016] Pid: 18013, comm: trinity Not tainted 3.3.0-rc1-next-20120123-sasha-dirty #134
[  200.752016] RIP: 0010:[<ffffffff825c3dd6>]  [<ffffffff825c3dd6>] get_cfcnfg+0x126/0x180
[  200.752016] RSP: 0018:ffff88000fbabb00  EFLAGS: 00010202
[  200.752016] RAX: 0000000000000001 RBX: 0000000000000016 RCX: 0000000000000000
[  200.752016] RDX: 0000000000000001 RSI: ffffffff8323c620 RDI: 0000000000000286
[  200.752016] RBP: ffff88000fbabb20 R08: 0000000000000003 R09: 0000000000000001
[  200.752016] R10: 0000000000000000 R11: 0000000000000001 R12: ffff88000502b480
[  200.752016] R13: ffffffff836b9440 R14: 0000000000000000 R15: 0000000000000010
[  200.752016] FS:  00007f6c3af86700(0000) GS:ffff880013a00000(0000) knlGS:0000000000000000
[  200.752016] CS:  0010 DS: 0000 ES: 0000 CR0: 000000008005003b
[  200.752016] CR2: 00007f7a60186f60 CR3: 000000000fb3b000 CR4: 00000000000406f0
[  200.752016] DR0: ffffffff810ab5e0 DR1: 0000000000000000 DR2: 0000000000000000
[  200.752016] DR3: 0000000000000000 DR6: 00000000ffff4ff0 DR7: 0000000000000600
[  200.752016] Process trinity (pid: 18013, threadinfo ffff88000fbaa000, task ffff880005002000)
[  200.752016] Stack:
[  200.752016]  ffffffff825c3cea ffffffff821cf0b0 ffff88000504d000 00000000ffffffd2
[  200.752016]  ffff88000fbabb80 ffffffff825c41be ffff88000fbabb80 0000000000000001
[  200.752016]  0000000000000001 ffff880005002000 ffff88000fbabb80 ffff88000504d000
[  200.752016] Call Trace:
[  200.752016]  [<ffffffff825c3cea>] ? get_cfcnfg+0x3a/0x180
[  200.752016]  [<ffffffff821cf0b0>] ? lockdep_rtnl_is_held+0x10/0x20
[  200.752016]  [<ffffffff825c41be>] caif_device_notify+0x2e/0x530
[  200.752016]  [<ffffffff810d61b7>] notifier_call_chain+0x67/0x110
[  200.752016]  [<ffffffff810d67c1>] raw_notifier_call_chain+0x11/0x20
[  200.752016]  [<ffffffff821bae82>] call_netdevice_notifiers+0x32/0x60
[  200.752016]  [<ffffffff821c2b26>] register_netdevice+0x196/0x300
[  200.752016]  [<ffffffff821c2ca9>] register_netdev+0x19/0x30
[  200.752016]  [<ffffffff81c1c67a>] loopback_net_init+0x4a/0xa0
[  200.752016]  [<ffffffff821b5e62>] ops_init+0x42/0x180
[  200.752016]  [<ffffffff821b600b>] setup_net+0x6b/0x100
[  200.752016]  [<ffffffff821b6466>] copy_net_ns+0x86/0x110
[  200.752016]  [<ffffffff810d5789>] create_new_namespaces+0xd9/0x190
[  200.752016]  [<ffffffff810d5964>] copy_namespaces+0x84/0xc0
[  200.752016]  [<ffffffff810aab0f>] copy_process+0xa2f/0x14c0
[  200.752016]  [<ffffffff810d54de>] ? up_read+0x1e/0x40
[  200.752016]  [<ffffffff810ab653>] do_fork+0x73/0x340
[  200.752016]  [<ffffffff8265f5fc>] ? __mutex_unlock_slowpath+0x10c/0x200
[  200.752016]  [<ffffffff8110c7bd>] ? trace_hardirqs_on+0xd/0x10
[  200.752016]  [<ffffffff82662add>] ? retint_swapgs+0x13/0x1b
[  200.752016]  [<ffffffff810554b3>] sys_clone+0x23/0x30
[  200.752016]  [<ffffffff82663743>] stub_clone+0x13/0x20
[  200.752016]  [<ffffffff826633b9>] ? system_call_fastpath+0x16/0x1b
[  200.752016] Code: dc 82 c6 05 71 b1 32 02 01 e8 47 dc b4 fe e9 6c ff ff ff 66 90 48 c7 c7 20 c6 23 83 e8 14 9e b4 fe 85 c0 0f 85 56 ff ff ff eb c4 <0f> 0b 80 3d 45 b1 32 02 01 90 0f 84 04 ff ff ff be f2 00 00 00
[  200.752016] RIP  [<ffffffff825c3dd6>] get_cfcnfg+0x126/0x180
[  200.752016]  RSP <ffff88000fbabb00>

Instead, we'll first check if the device in the notification is a CAIF device:
 - If it is - the net generic struct in that namespace must have been already
initialized.
 - If not - just ignore it as we don't care about other devices.

Signed-off-by: Sasha Levin <redacted>
---
 net/caif/caif_dev.c |    5 ++++-
 1 files changed, 4 insertions(+), 1 deletions(-)
diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c
index 673728a..75b9803 100644
--- a/net/caif/caif_dev.c
+++ b/net/caif/caif_dev.c
@@ -372,13 +372,16 @@ static int caif_device_notify(struct notifier_block *me, unsigned long what,
 	int head_room = 0;
 	struct caif_device_entry_list *caifdevs;
 
+	if (dev->type != ARPHRD_CAIF)
+		return 0;
+
 	cfg = get_cfcnfg(dev_net(dev));
 	caifdevs = caif_device_list(dev_net(dev));
 	if (!cfg || !caifdevs)
 		return 0;
 
 	caifd = caif_get(dev);
-	if (caifd == NULL && dev->type != ARPHRD_CAIF)
+	if (caifd == NULL)
 		return 0;
 
 	switch (what) {
-- 
1.7.8.3

Re: [PATCH] net: caif: Don't act on notification for non-caif devices

From: Sjur Brændeland <hidden>
Date: 2012-01-24 10:52:30

Hi Sasha,
Since the list of CAIF devices is stored in the net generic struct in each
net namespace, which is not initialized at that point, we see the following
BUG():

[  200.752016] kernel BUG at include/net/netns/generic.h:40!
...
[  200.752016] Call Trace:
[  200.752016]  [<ffffffff825c3cea>] ? get_cfcnfg+0x3a/0x180
[  200.752016]  [<ffffffff821cf0b0>] ? lockdep_rtnl_is_held+0x10/0x20
[  200.752016]  [<ffffffff825c41be>] caif_device_notify+0x2e/0x530
Argh, my bad. This issue has been identified and fixed by David
Woodhouse earlier,
but was reintroduced again by me when adding support for CAIF over NCM.
The CAIF code is handling if net_generic() returns NULL, but I missed that
net_generic() does BUG_ON().
Instead, we'll first check if the device in the notification is a CAIF device:
 - If it is - the net generic struct in that namespace must have been already
initialized.
 - If not - just ignore it as we don't care about other devices.

Signed-off-by: Sasha Levin <redacted>
Nack, we have to handle other device types than just ARPHDR_CAIF after
introducing
CAIF over USB/NCM. I'd rather fix this in netns by removing the BUG_ON
and return
NULL. How about this instead:
diff --git a/include/net/netns/generic.h b/include/net/netns/generic.h
index 3419bf5..0fc2eea 100644
--- a/include/net/netns/generic.h
+++ b/include/net/netns/generic.h
@@ -37,8 +37,10 @@ static inline void *net_generic(const struct net *net, int id

        rcu_read_lock();
        ng = rcu_dereference(net->gen);
-       BUG_ON(id == 0 || id > ng->len);
-       ptr = ng->ptr[id - 1];
+       if (id == 0 || id > ng->len)
+               ptr = NULL;
+       else
+               ptr = ng->ptr[id - 1];
        rcu_read_unlock();

        return ptr;
I'll post a patch for this soon.

Regards,
Sjur

Re: [PATCH] net: caif: Don't act on notification for non-caif devices

From: Sasha Levin <hidden>
Date: 2012-01-24 12:49:53

On Tue, 2012-01-24 at 11:52 +0100, Sjur Brændeland wrote:
Nack, we have to handle other device types than just ARPHDR_CAIF after
introducing
CAIF over USB/NCM. I'd rather fix this in netns by removing the BUG_ON
and return
NULL. How about this instead: 
[snip]

I think that doing it this way is wrong for two reasons:

1. The code in net/ assumes net_generic is a trivial dereference and doesn't check that it's not NULL. This means that if anything goes wrong there you'll have a more dangerous NULL deref instead of a BUG().

2. You'll need to add other device to that if() statement anyway, as it currently looks like this:

	cfg = get_cfcnfg(dev_net(dev));
	caifdevs = caif_device_list(dev_net(dev));
	if (!cfg || !caifdevs)
		return 0;

	caifd = caif_get(dev);
	if (caifd == NULL && dev->type != ARPHRD_CAIF)
		return 0;

What my patch did was simply move the type check to above the net_generic call, it didn't add any new checks - which according to what you said, you'll need to do anyway.

-- 

Sasha.

Re: [PATCH] net: caif: Don't act on notification for non-caif devices

From: Sjur Brændeland <hidden>
Date: 2012-01-24 15:06:35

Hi Sasha,
quoted
Nack, we have to handle other device types than just ARPHDR_CAIF after
introducing CAIF over USB/NCM.
What my patch did was simply move the type check to above the net_generic call,
it didn't add any new checks - which according to what you said, you'll need to do anyway.
As I said I, don't think your patch would work. Try to see what happens if
dev->type != ARPHDR_CAIF and caifd != NULL. Then the statement:

	if (caifd == NULL && dev->type != ARPHRD_CAIF)
		return 0;
is very different from:

       if (dev->type != ARPHRD_CAIF)
               return 0;
...
       if (caifd == NULL)
               return 0;

Anyway, another option could be to explicitly check if name space is
initialized,
similar to what net_generic() does,e.g. something like:
diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c
index 673728a..3197bc2 100644
--- a/net/caif/caif_dev.c
+++ b/net/caif/caif_dev.c
@@ -371,6 +371,13 @@ static int caif_device_notify(struct notifier_block *me, un
        struct cflayer *layer, *link_support;
        int head_room = 0;
        struct caif_device_entry_list *caifdevs;
+       int len;
+
+       rcu_read_lock();
+       len = rcu_dereference(dev_net(dev)->gen)->len;
+       rcu_read_unlock();
+       if (caif_net_id > len)
+               return 0;

        cfg = get_cfcnfg(dev_net(dev));
        caifdevs = caif_device_list(dev_net(dev));
--
Regards,
Sjur

Re: [PATCH] net: caif: Don't act on notification for non-caif devices

From: Sasha Levin <hidden>
Date: 2012-01-24 15:23:48

On Tue, Jan 24, 2012 at 10:06 AM, Sjur Brændeland [off-list ref] wrote:
Hi Sasha,
quoted
quoted
Nack, we have to handle other device types than just ARPHDR_CAIF after
introducing CAIF over USB/NCM.
What my patch did was simply move the type check to above the net_generic call,
it didn't add any new checks - which according to what you said, you'll need to do anyway.
As I said I, don't think your patch would work. Try to see what happens if
dev->type != ARPHDR_CAIF and caifd != NULL. Then the statement:

       if (caifd == NULL && dev->type != ARPHRD_CAIF)
               return 0;
is very different from:

      if (dev->type != ARPHRD_CAIF)
              return 0;
...
      if (caifd == NULL)
              return 0;
Right.
quoted hunk
Anyway, another option could be to explicitly check if name space is
initialized,
similar to what net_generic() does,e.g. something like:
diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c
index 673728a..3197bc2 100644
--- a/net/caif/caif_dev.c
+++ b/net/caif/caif_dev.c
@@ -371,6 +371,13 @@ static int caif_device_notify(struct notifier_block *me, un
       struct cflayer *layer, *link_support;
       int head_room = 0;
       struct caif_device_entry_list *caifdevs;
+       int len;
+
+       rcu_read_lock();
+       len = rcu_dereference(dev_net(dev)->gen)->len;
+       rcu_read_unlock();
+       if (caif_net_id > len)
+               return 0;

       cfg = get_cfcnfg(dev_net(dev));
       caifdevs = caif_device_list(dev_net(dev));
We could, in that case we'd just need to handle the case where it was
initialized by a device with higher id than CAIF (which we already do
I think), and do it without touching net_generic structure directly.

btw, Why do we store the devices per-namespace instead of globally? Is
it such a big benefit in performance?
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help