* tc: RTM_GETQDISC causes kernel OOPS
@ 2010-05-21 22:42 Ben Pfaff
2010-05-22 7:18 ` Eric Dumazet
0 siblings, 1 reply; 6+ messages in thread
From: Ben Pfaff @ 2010-05-21 22:42 UTC (permalink / raw)
To: Jamal Hadi Salim; +Cc: netdev
Hi. While working on some library code for working with qdiscs and
classes I came upon a kernel OOPS. Originally I came across it with a
2.6.26 kernel, but I can also reproduce it with unmodified v2.6.34 from
kernel.org.
At the end of this mail I'm appending both an example of the OOPS and a
simple test program that reliably reproduces the problem for me when I
invoke it with "lo" as argument. The program does not need to be run as
root.
After the OOPS, a lot of networking and other system functions stop
working, so it seems to me a serious issue.
The null pointer dereference that causes the OOPS is the dereference of
the return value of qdisc_dev() in tc_fill_qdisc() in
net/sched/sch_api.c line 1163:
1161 tcm->tcm__pad1 = 0;
1162 tcm->tcm__pad2 = 0;
1163 tcm->tcm_ifindex = qdisc_dev(q)->ifindex;
1164 tcm->tcm_parent = clid;
1165 tcm->tcm_handle = q->handle;
I am pretty sure about that, because if I add "WARN_ON(!qdisc_dev(q));"
just before line 1163 then that warning triggers.
Thanks,
Ben.
----------------------------------------------------------------------
BUG: unable to handle kernel NULL pointer dereference at 00000050
IP: [<c12280c0>] tc_fill_qdisc+0x68/0x1e5
*pde = 00000000
Oops: 0000 [#1] SMP
last sysfs file:
Modules linked in:
Pid: 600, comm: qdisc Not tainted 2.6.34 #16 /
EIP: 0060:[<c12280c0>] EFLAGS: 00010282 CPU: 0
EIP is at tc_fill_qdisc+0x68/0x1e5
EAX: 00000000 EBX: ffffffff ECX: 00000000 EDX: c7222070
ESI: c14576e0 EDI: c7115200 EBP: c7239ca0 ESP: c7239c3c
DS: 007b ES: 007b FS: 00d8 GS: 0033 SS: 0068
Process qdisc (pid: 600, ti=c7239000 task=c720b700 task.ti=c7239000)
Stack:
00000024 00000014 00000000 c14323a0 c7222060 c7222060 c10a7abd 00001030
<0> 000000d0 c7222060 000000d0 c1228329 000000d0 00000fc4 000000d0 c7115200
<0> 000000d0 00000ec0 c7239cac c12104b1 00000ec0 c1457a98 c7115200 00000258
Call Trace:
[<c10a7abd>] ? __kmalloc_track_caller+0x122/0x131
[<c1228329>] ? qdisc_notify+0x2a/0xc8
[<c12104b1>] ? __alloc_skb+0x4e/0x115
[<c122838a>] ? qdisc_notify+0x8b/0xc8
[<c12287ea>] ? tc_get_qdisc+0x143/0x15d
[<c12286a7>] ? tc_get_qdisc+0x0/0x15d
[<c1220c28>] ? rtnetlink_rcv_msg+0x195/0x1af
[<c1220a93>] ? rtnetlink_rcv_msg+0x0/0x1af
[<c12329e2>] ? netlink_rcv_skb+0x30/0x75
[<c1220a8b>] ? rtnetlink_rcv+0x1e/0x26
[<c123281b>] ? netlink_unicast+0xc4/0x11a
[<c1232eda>] ? netlink_sendmsg+0x223/0x230
[<c120978c>] ? sock_sendmsg+0xa8/0xbf
[<c10514fc>] ? print_lock_contention_bug+0x14/0xd7
[<c101ebe4>] ? __wake_up+0x15/0x3b
[<c101ebe4>] ? __wake_up+0x15/0x3b
[<c101ec00>] ? __wake_up+0x31/0x3b
[<c10acda4>] ? fget_light+0x2d/0xaf
[<c109488d>] ? might_fault+0x47/0x81
[<c120acf9>] ? sys_sendto+0xa4/0xc0
[<c116b82c>] ? _copy_from_user+0x2e/0x108
[<c120ad92>] ? sys_connect+0x63/0x6e
[<c120ad2d>] ? sys_send+0x18/0x1a
[<c120aedb>] ? sys_socketcall+0xd4/0x1a5
[<c12ca931>] ? syscall_call+0x7/0xb
Code: 50 8b 55 08 89 f8 6a 14 ff 75 14 e8 49 fa ff ff 89 c2 83 c2 10 89 45 ac c6 42 01 00 66 c7 42 02 00 00 c6 40 10 00 8b 46 40 8b 00 <8b> 40 50 89 5a 0c 89 42 04 8b 46 20 89 42 08 8b 46 28 89 42 10
EIP: [<c12280c0>] tc_fill_qdisc+0x68/0x1e5 SS:ESP 0068:c7239c3c
CR2: 0000000000000050
---[ end trace 6fb85bbc66de8f42 ]---
----------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <linux/rtnetlink.h>
#include <linux/pkt_sched.h>
#include <net/if.h>
int
main(int argc, char *argv[])
{
struct {
struct nlmsghdr nlmsg;
struct tcmsg tcmsg;
} msg;
struct sockaddr_nl local, remote;
int ifindex;
int fd;
if (argc != 2) {
fprintf(stderr,
"usage: %s <netdev>\n"
"where <netdev> is a network device, e.g. \"lo\"\n",
argv[0]);
return EXIT_FAILURE;
}
/* Get ifindex. */
ifindex = if_nametoindex(argv[1]);
if (!ifindex) {
fprintf(stderr, "no network device named \"%s\"", argv[1]);
return EXIT_FAILURE;
}
/* Make rtnetlink socket. */
fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (fd < 0) {
perror("socket");
return EXIT_FAILURE;
}
/* Bind local address as our selected pid. */
memset(&local, 0, sizeof local);
local.nl_family = AF_NETLINK;
local.nl_pid = getpid();
if (bind(fd, (struct sockaddr *) &local, sizeof local) < 0) {
perror("bind");
return EXIT_FAILURE;
}
/* Bind remote address as the kernel (pid 0). */
memset(&remote, 0, sizeof remote);
remote.nl_family = AF_NETLINK;
remote.nl_pid = 0;
if (connect(fd, (struct sockaddr *) &remote, sizeof remote) < 0) {
perror("connect");
return EXIT_FAILURE;
}
/* Send "get" request. */
memset(&msg, 0, sizeof msg);
msg.nlmsg.nlmsg_len = sizeof msg;
msg.nlmsg.nlmsg_type = RTM_GETQDISC;
msg.nlmsg.nlmsg_flags = NLM_F_REQUEST | NLM_F_ECHO | NLM_F_ACK;
msg.nlmsg.nlmsg_seq = 1;
msg.nlmsg.nlmsg_pid = getpid();
msg.tcmsg.tcm_family = AF_UNSPEC;
msg.tcmsg.tcm_ifindex = ifindex;
msg.tcmsg.tcm_handle = 0;
msg.tcmsg.tcm_parent = TC_H_ROOT;
if (send(fd, &msg, sizeof msg, 0) < 0) {
perror("send");
return EXIT_FAILURE;
}
return 0;
}
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: tc: RTM_GETQDISC causes kernel OOPS
2010-05-21 22:42 tc: RTM_GETQDISC causes kernel OOPS Ben Pfaff
@ 2010-05-22 7:18 ` Eric Dumazet
2010-05-22 9:51 ` Patrick McHardy
0 siblings, 1 reply; 6+ messages in thread
From: Eric Dumazet @ 2010-05-22 7:18 UTC (permalink / raw)
To: Ben Pfaff; +Cc: Jamal Hadi Salim, netdev, Patrick McHardy
Le vendredi 21 mai 2010 à 15:42 -0700, Ben Pfaff a écrit :
> Hi. While working on some library code for working with qdiscs and
> classes I came upon a kernel OOPS. Originally I came across it with a
> 2.6.26 kernel, but I can also reproduce it with unmodified v2.6.34 from
> kernel.org.
>
> At the end of this mail I'm appending both an example of the OOPS and a
> simple test program that reliably reproduces the problem for me when I
> invoke it with "lo" as argument. The program does not need to be run as
> root.
>
> After the OOPS, a lot of networking and other system functions stop
> working, so it seems to me a serious issue.
>
> The null pointer dereference that causes the OOPS is the dereference of
> the return value of qdisc_dev() in tc_fill_qdisc() in
> net/sched/sch_api.c line 1163:
>
> 1161 tcm->tcm__pad1 = 0;
> 1162 tcm->tcm__pad2 = 0;
> 1163 tcm->tcm_ifindex = qdisc_dev(q)->ifindex;
> 1164 tcm->tcm_parent = clid;
> 1165 tcm->tcm_handle = q->handle;
>
> I am pretty sure about that, because if I add "WARN_ON(!qdisc_dev(q));"
> just before line 1163 then that warning triggers.
>
> Thanks,
Indeed, thanks for this very useful report !
We could add a check for TCQ_F_BUILTIN flag, or just make
qdisc_notify() checks consistent for both old and new qdisc
What other people thinks ?
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index fe35c1f..e454c73 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -1210,7 +1210,7 @@ static int qdisc_notify(struct net *net, struct sk_buff *oskb,
if (tc_fill_qdisc(skb, old, clid, pid, n->nlmsg_seq, 0, RTM_DELQDISC) < 0)
goto err_out;
}
- if (new) {
+ if (new && new->handle) {
if (tc_fill_qdisc(skb, new, clid, pid, n->nlmsg_seq, old ? NLM_F_REPLACE : 0, RTM_NEWQDISC) < 0)
goto err_out;
}
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: tc: RTM_GETQDISC causes kernel OOPS
2010-05-22 7:18 ` Eric Dumazet
@ 2010-05-22 9:51 ` Patrick McHardy
2010-05-22 12:31 ` jamal
0 siblings, 1 reply; 6+ messages in thread
From: Patrick McHardy @ 2010-05-22 9:51 UTC (permalink / raw)
To: Eric Dumazet; +Cc: Ben Pfaff, Jamal Hadi Salim, netdev
Eric Dumazet wrote:
> Le vendredi 21 mai 2010 à 15:42 -0700, Ben Pfaff a écrit :
>> Hi. While working on some library code for working with qdiscs and
>> classes I came upon a kernel OOPS. Originally I came across it with a
>> 2.6.26 kernel, but I can also reproduce it with unmodified v2.6.34 from
>> kernel.org.
>>
>> At the end of this mail I'm appending both an example of the OOPS and a
>> simple test program that reliably reproduces the problem for me when I
>> invoke it with "lo" as argument. The program does not need to be run as
>> root.
>>
>> After the OOPS, a lot of networking and other system functions stop
>> working, so it seems to me a serious issue.
>>
>> The null pointer dereference that causes the OOPS is the dereference of
>> the return value of qdisc_dev() in tc_fill_qdisc() in
>> net/sched/sch_api.c line 1163:
>>
>> 1161 tcm->tcm__pad1 = 0;
>> 1162 tcm->tcm__pad2 = 0;
>> 1163 tcm->tcm_ifindex = qdisc_dev(q)->ifindex;
>> 1164 tcm->tcm_parent = clid;
>> 1165 tcm->tcm_handle = q->handle;
>>
>> I am pretty sure about that, because if I add "WARN_ON(!qdisc_dev(q));"
>> just before line 1163 then that warning triggers.
>>
>> Thanks,
>
> Indeed, thanks for this very useful report !
>
> We could add a check for TCQ_F_BUILTIN flag, or just make
> qdisc_notify() checks consistent for both old and new qdisc
>
> What other people thinks ?
We already use TCQ_F_BUILTIN in tc_qdisc_dump_ignore(), so I
think it would be more consistent than checking for a handle
to use it here as well.
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: tc: RTM_GETQDISC causes kernel OOPS
2010-05-22 9:51 ` Patrick McHardy
@ 2010-05-22 12:31 ` jamal
2010-05-23 6:37 ` [PATCH] net_sched: Fix qdisc_notify() Eric Dumazet
0 siblings, 1 reply; 6+ messages in thread
From: jamal @ 2010-05-22 12:31 UTC (permalink / raw)
To: Patrick McHardy; +Cc: Eric Dumazet, Ben Pfaff, netdev
On Sat, 2010-05-22 at 11:51 +0200, Patrick McHardy wrote:
>
> We already use TCQ_F_BUILTIN in tc_qdisc_dump_ignore(), so I
> think it would be more consistent than checking for a handle
> to use it here as well.
Agree - it is more semantically correct..
I wonder though if it is better to do the check in tc_get_qdisc()
and tc_modify_qdisc()?
cheers,
jamal
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH] net_sched: Fix qdisc_notify()
2010-05-22 12:31 ` jamal
@ 2010-05-23 6:37 ` Eric Dumazet
2010-05-24 6:11 ` David Miller
0 siblings, 1 reply; 6+ messages in thread
From: Eric Dumazet @ 2010-05-23 6:37 UTC (permalink / raw)
To: hadi, David Miller; +Cc: Patrick McHardy, Ben Pfaff, netdev
Le samedi 22 mai 2010 à 08:31 -0400, jamal a écrit :
> On Sat, 2010-05-22 at 11:51 +0200, Patrick McHardy wrote:
>
> >
> > We already use TCQ_F_BUILTIN in tc_qdisc_dump_ignore(), so I
> > think it would be more consistent than checking for a handle
> > to use it here as well.
>
> Agree - it is more semantically correct..
>
> I wonder though if it is better to do the check in tc_get_qdisc()
> and tc_modify_qdisc()?
Lets check tc_qdisc_dump_ignore() before tc_fill_qdisc(), its more
consistent ;)
David, this is a stable candidate (2.6.26 and up)
Thanks
[PATCH] net_sched: Fix qdisc_notify()
Ben Pfaff reported a kernel oops and provided a test program to
reproduce it.
https://kerneltrap.org/mailarchive/linux-netdev/2010/5/21/6277805
tc_fill_qdisc() should not be called for builtin qdisc, or it
dereference a NULL pointer to get device ifindex.
Fix is to always use tc_qdisc_dump_ignore() before calling
tc_fill_qdisc().
Reported-by: Ben Pfaff <blp@nicira.com>
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
net/sched/sch_api.c | 14 +++++++-------
1 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index fe35c1f..b9e8c3b 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -1195,6 +1195,11 @@ nla_put_failure:
return -1;
}
+static bool tc_qdisc_dump_ignore(struct Qdisc *q)
+{
+ return (q->flags & TCQ_F_BUILTIN) ? true : false;
+}
+
static int qdisc_notify(struct net *net, struct sk_buff *oskb,
struct nlmsghdr *n, u32 clid,
struct Qdisc *old, struct Qdisc *new)
@@ -1206,11 +1211,11 @@ static int qdisc_notify(struct net *net, struct sk_buff *oskb,
if (!skb)
return -ENOBUFS;
- if (old && old->handle) {
+ if (old && !tc_qdisc_dump_ignore(old)) {
if (tc_fill_qdisc(skb, old, clid, pid, n->nlmsg_seq, 0, RTM_DELQDISC) < 0)
goto err_out;
}
- if (new) {
+ if (new && !tc_qdisc_dump_ignore(new)) {
if (tc_fill_qdisc(skb, new, clid, pid, n->nlmsg_seq, old ? NLM_F_REPLACE : 0, RTM_NEWQDISC) < 0)
goto err_out;
}
@@ -1223,11 +1228,6 @@ err_out:
return -EINVAL;
}
-static bool tc_qdisc_dump_ignore(struct Qdisc *q)
-{
- return (q->flags & TCQ_F_BUILTIN) ? true : false;
-}
-
static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb,
struct netlink_callback *cb,
int *q_idx_p, int s_q_idx)
^ permalink raw reply related [flat|nested] 6+ messages in thread
end of thread, other threads:[~2010-05-24 6:11 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-05-21 22:42 tc: RTM_GETQDISC causes kernel OOPS Ben Pfaff
2010-05-22 7:18 ` Eric Dumazet
2010-05-22 9:51 ` Patrick McHardy
2010-05-22 12:31 ` jamal
2010-05-23 6:37 ` [PATCH] net_sched: Fix qdisc_notify() Eric Dumazet
2010-05-24 6:11 ` David Miller
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).