kern/50951: kernel ran out of mbufs, m_copy() failed and the return
value wasn't checked before dereferencing the pointer
Evan Oldford
eoldford at verniernetworks.com
Mon Apr 14 10:10:03 PDT 2003
>Number: 50951
>Category: kern
>Synopsis: kernel ran out of mbufs, m_copy() failed and the return value wasn't checked before dereferencing the pointer
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: freebsd-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: sw-bug
>Submitter-Id: current-users
>Arrival-Date: Mon Apr 14 10:10:00 PDT 2003
>Closed-Date:
>Last-Modified:
>Originator: Evan Oldford
>Release: FreeBSD 4.7-RELEASE i386
>Organization:
Vernier Networks
>Environment:
System: FreeBSD net7-20.eoldford.com 4.7-p1-RELEASE FreeBSD 4.7-p1-RELEASE #2: Fri Apr 11 12:01:39 PDT 2003 root at lax.verniernetworks.com:/usr/build/ambit2-3.1/freebsd/sys/compile/AMBIT i386
>Description:
Summary from Mark Gooderum:
This is a FreeBSD bug. The debug info is off by one line on the source
- not uncommon for optimized code. The fault is actually on line 352
of file sys/net/if_ethersubr.c.
struct mbuf *n = m_copy(m, 0, (int)M_COPYALL);
n->m_pkthdr.csum_flags |= csum_flags; <==== Dies Here
if (csum_flags & CSUM_DATA_VALID)
n->m_pkthdr.csum_data = 0xffff;
(void) if_simloop(ifp, n, dst->sa_family, hlen);
The fault address is 0x20:
Fatal trap 12: page fault while in kernel mode
fault virtual address = 0x20
fault code = supervisor write, page not present
instruction pointer = 0x8:0xc019137c
stack pointer = 0x10:0xc02cc4a8
frame pointer = 0x10:0xc02cc4e4
code segment = base 0x0, limit 0xfffff, type 0x1b
= DPL 0, pres 1, def32 1, gran 1
processor eflags = interrupt enabled, resume, IOPL = 0
current process = Idle
interrupt mask =
trap number = 12
panic: page fault
All mbufs exhausted, please see tuning(7).
The return is always in %eax (x86 ABI) - the compiler moves this to %ebx
and then does an indexed reference of 0x20 off of this address - which
is the offset of the copy_flags field. The %ebx is holding the
registerized csum_flags variable. So that's why we know this is line 352:
#6 */ 0xc019137c/* in ether_output (ifp=0xc1d56400, m=0xc0786600,
dst=0xc02cc568,
rt0=0xc1e67500) at ../../net/if_ethersubr.c:350
0xc0191369 <ether_output+729>: push $0x1
0xc019136b <ether_output+731>: push $0x3b9aca00
0xc0191370 <ether_output+736>: push $0x0
0xc0191372 <ether_output+738>: pushl 0xc(%ebp)
0xc0191375 <ether_output+741>: call 0xc0170b10 <m_copym>
0xc019137a <ether_output+746>: mov %eax,%edx
*/0xc019137c/* <ether_output+748>: or %ebx,0x20(%edx)
0xc019137f <ether_output+751>: add $0x10,%esp
0xc0191382 <ether_output+754>: test $0x4,%bh
struct mbuf *n = m_copy(m, 0, (int)M_COPYALL);
*/n->m_pkthdr.csum_flags |= csum_flags; <=== 352/*
if (csum_flags & CSUM_DATA_VALID)
n->m_pkthdr.csum_data = 0xffff;
(void) if_simloop(ifp, n, dst->sa_family, hlen);
We also have a kernel printf of out of mbufs right before the panic
(which would have been emitted by the m_copym() (m_copy is a macro
around m_copym). So the kernel ran out of mbufs, m_copy() failed and
the return value wasn't checked before dereferencing the pointer. The
code of 352-354 was added January 2002 (to 1.70.2.23) - before that it
would have triggered a KASSERT() in if_simloop() - if you had a debug
load - or just a crash if not. That code was added in June of 1998 when
there was general cleanup of the loopback - so the bug has been latent
since FreeBSD 3.1.
The backtrace:
(kgdb) where
#0 dumpsys () at ../../kern/kern_shutdown.c:504
#1 0xc01563a5 in boot (howto=260) at ../../kern/kern_shutdown.c:324
#2 0xc0156841 in panic (fmt=0xc02b29ac "%s") at ../../kern/kern_shutdown.c:634
#3 0xc023867f in trap_fatal (frame=0xc02cc468, eva=32)
at ../../i386/i386/trap.c:974
#4 0xc023832d in trap_pfault (frame=0xc02cc468, usermode=0, eva=32)
at ../../i386/i386/trap.c:867
#5 0xc0237ea7 in trap (frame={tf_fs = 6422544, tf_es = 16, tf_ds = 6422544,
tf_edi = 1, tf_esi = -1070807824, tf_ebp = -1070807836,
tf_isp = -1070807916, tf_ebx = 0, tf_edx = 0, tf_ecx = 0, tf_eax = 0,
tf_trapno = 12, tf_err = 2, tf_eip = -1072098436, tf_cs = 8,
tf_eflags = 66118, tf_esp = -1065851392, tf_ss = 0})
at ../../i386/i386/trap.c:466
#6 0xc019137c in ether_output (ifp=0xc1d56400, m=0xc0786600, dst=0xc02cc568,
rt0=0xc1e67500) at ../../net/if_ethersubr.c:350
#7 0xc01ae0ac in ip_output (m0=0xc0786600, opt=0x0, ro=0xc02cc564, flags=34,
imo=0x0, inp=0x0) at ../../netinet/ip_output.c:1047
#8 0xc0254d7b in ext_iface_write_dsock (pktp=0xc02cc600, freepkt=0,
sin=0xc03022c0) at ../../ambitsrc/combined/ext_iface.c:468
#9 0xc0254a9f in ext_iface_send_ip (pkt=0xc2001880)
at ../../ambitsrc/combined/ext_iface.c:309
#10 0xc02576e8 in int_iface_deliver (pkt=0xc2001880, client=0xc1e6a200,
tun_ip={s_addr = 0}, was_tunneled=0)
at ../../ambitsrc/combined/int_iface.c:1846
#11 0xc02574de in int_iface_outgoing_step_3 (client=0xc1e6a200,
pkt=0xc2001880, tun_ip={s_addr = 0})
at ../../ambitsrc/combined/int_iface.c:1684
#12 0xc02568ab in int_iface_outgoing_step_2 (client=0xc1e6a200,
pkt=0xc2001880, encrypted={s_addr = 0})
at ../../ambitsrc/combined/int_iface.c:993
#13 0xc0256455 in int_iface_outgoing_step_1 (client=0xc1e6a200, pkt=0xc2001880)
at ../../ambitsrc/combined/int_iface.c:713
#14 0xc0256178 in int_iface_process_in_pkt (iface=0xc1d97800, pkt=0xc2001880,
outgoing=0, vlan_id=65535) at ../../ambitsrc/combined/int_iface.c:563
#15 0xc02628db in ng_ambit_rx_int (hook=0xc1d6d1c0, m=0xc0657500, meta=0x0,
link_num=2) at ../../ambitsrc/ng_ambit/ng_ambit.c:1274
#16 0xc0261f52 in ng_ambit_rcvdata (hook=0xc1d6d1c0, m=0xc0657500, meta=0x0)
at ../../ambitsrc/ng_ambit/ng_ambit.c:608
#17 0xc0197eb1 in ng_send_dataq (hook=0xc1d6d1e0, m=0xc0657500, meta=0x0)
at ../../netgraph/ng_base.c:1676
#18 0xc019836f in ngintr () at ../../netgraph/ng_base.c:2011
#19 0xc022ce19 in swi_net_next ()
>How-To-Repeat:
This is very hard to reproduce. You need your system to run
out of mbufs while receiving broadcast messages--dhcp request.
>Fix:
Here's a patch that does the appropriate error checking.
--- sys/net/if_ethersubr.c.orig Wed Feb 12 13:03:23 2003
+++ sys/net/if_ethersubr.c Thu Apr 10 13:05:52 2003
@@ -349,6 +349,12 @@
if ((m->m_flags & M_BCAST) || (loop_copy > 0)) {
struct mbuf *n = m_copy(m, 0, (int)M_COPYALL);
+ /* m_copy failed */
+ if (n == NULL) {
+ error = 0;
+ goto bad;
+ }
+
n->m_pkthdr.csum_flags |= csum_flags;
if (csum_flags & CSUM_DATA_VALID)
n->m_pkthdr.csum_data = 0xffff;
>Release-Note:
>Audit-Trail:
>Unformatted:
More information about the freebsd-bugs
mailing list