Linux Kernel Memory Leak Detection: Catalin Marinas Linuxcon Europe 2011
Linux Kernel Memory Leak Detection: Catalin Marinas Linuxcon Europe 2011
Linux Kernel Memory Leak Detection: Catalin Marinas Linuxcon Europe 2011
Kernel
Memory
Leak
Detection
Catalin
Marinas
LinuxCon
Europe
2011
Agenda
Introduc8on
Overview
Object
Tracking
Memory
Scanning
Limita8ons
Usage
Tips
Example
Introduction
First
kmemleak
patches
posted
on
LKML
January
2006
Overview
Memory
leak
example:
device->name = kstrdup(device_path, GFP_NOFS);
ret = find_next_devid(root, &device->devid);
if (ret) {
kfree(device);
return ret;
}
Overview
(contd)
Kmemleak
is
similar
to
a
tracing
garbage
collector
using
tri-
colour
marking
(Wikipedia
hWp://bit.ly/q2cSle)
White:
objects
that
could
be
memory
leaks
Grey:
objects
known
not
to
be
memory
leaks
Black:
objects
that
have
no
references
to
other
objects
in
the
white
set
Kmemleak
tracks
objects
allocated
via
kmalloc,
kmem_cache_alloc,
vmalloc,
alloc_bootmem
and
pcpu_alloc
allocators
Object
Tracking
Kernel
memory
alloca8ons
are
recorded
by
kmemleak
It
is
important
that
all
memory
alloca8ons
are
tracked
to
avoid
false
posi8ves
early_log[CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE]
array
The
early
log
is
replayed
ager
kmemleak
is
ini8alized
Memory
Scanning
Kmemleak
thread
scans
the
memory
periodically
to
iden8fy
object
referencing
(graph)
There
is
no
type
informa8on
for
memory
loca8ons
An
address
can
point
anywhere
inside
an
object
(list_head)
Kmemleak
API
allows
scanning
of
specic
areas
within
an
object
LIST_HEAD(test_list)
next
prev
struct test_node
header[25]
list_head
...
header[25]
list_head
next
next
prev
prev
footer[25]
footer[25]
Limitations
False
nega8ves
Leaks
may
be
hidden
by
memory
loca8ons
looking
like
real
addresses
Type
iden8ca8on
is
not
possible
Task
stacks
have
many
address-like
values
The
leak
will
eventually
be
found
if
running
for
long
enough
or
on
a
wider
range
of
plaiorms
False
posi8ves
Objects
falsely
reported
as
leaks
Usually
for
objects
referenced
from
other
objects
that
are
not
tracked
by
kmemleak
(like
page
alloca8ons)
Object
referenced
via
a
modied
pointer
(like
physical
address)
API
provided
for
annota8ng
false
posi8ves
Limitations
(contd)
Does
not
allow
overlapping
objects
All
pointers
must
be
real
addresses
in
the
kernel
virtual
space
Per-CPU
alloca8ons
are
scanned
but
never
considered
leaks
Non-virtual
address
pointers
cannot
be
tracked
(IOMMU
etc.)
ioremap
mappings
are
not
tracked
Usage
Kmemleak
API
described
in
Documenta8on/kmemleak.txt
CONFIG_DEBUG_KMEMLEAK=y
CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=400
(default)
Alloca8on/freeing
API
Usage
(contd)
False
nega8ve
reduc8on
API
kmemleak_scan_area:
only
scan
an
object
area
kmemleak_no_scan:
do
not
scan
the
object
kmemleak_ignore:
do
not
scan
or
report
an
object
as
leak
kmemleak_erase:
erase
a
pointer
variable
Usage
(contd)
stack=on|off:
enable|disable
task
stack
scanning
(default
on)
scan=on|off:
enable|disable
the
scanning
thread
(default
on)
scan=<secs>:
set
the
scanning
period
(default
600)
scan:
trigger
a
memory
scan
clear:
clear
the
list
of
memory
leaks
reported
(current
white
Tips
Example
kmemleak: 1 new suspected memory leaks (see /sys/kernel/
debug/kmemleak)
# cat /sys/kernel/debug/kmemleak
unreferenced object 0xef42d000 (size 28):
comm "khubd", pid 189, jiffies 4294937550 (age 2543.370s)
hex dump (first 28 bytes):
00 01 10 00 00 02 20 00 08 d0 42 ef 08 d0 42 ef
00 00 00 00 00 00 00 00 ff ff ff ff
Slab
backtrace:
allocator
[<c0080fe1>] create_object+0xa1/0x1ac
invoked
[<c007eac5>] kmem_cache_alloc+0x8d/0xdc
[<c01a966d>] isp1760_urb_enqueue+0x2f9/0x358
[<c019bbbd>] usb_hcd_submit_urb+0x75/0x574
[<c019d8f1>] usb_start_wait_urb+0x29/0x80
[<c019daad>] usb_control_msg+0x89/0xac
[<c0197f43>] hub_port_init+0x4fb/0x9c8
Example
(contd)
# addr2line i f e vmlinux c01a966d
qh_alloc
drivers/usb/host/isp1760-hcd.c:382
isp1760_urb_enqueue
drivers/usb/host/isp1760-hcd.c:1531
# vi drivers/usb/host/isp1760-hcd.c +1531
...
ep_queue = &priv->controlqhs;
...
qh = qh_alloc(GFP_ATOMIC);
if (!qh) {
retval = -ENOMEM;
goto out;
}
list_add_tail(&qh->qh_list, ep_queue);
urb->ep->hcpriv = qh;
Inlined
func8on
Pointer
stored
Example
(contd)
# grep n list_del drivers/usb/host/isp1760-hcd.c
1017: list_del(&qh->qh_list);
Object
# vi drivers/usb/host/isp1760-hcd.c +1017
removed
void schedule_ptds(struct usb_hcd *hcd)
from
list
...
list_for_each_entry_safe(qh, ...) {
Condi8on
...
false
list_del(&qh->qh_list);
if (ep->hcpriv == NULL) {
/* Endpoint has been disabled, so we
can free the associated queue head. */
qh_free(qh);
}
Func8on
...
not
called
}
Example
(contd)
# grep n hcpriv drivers/usb/host/isp1760-hcd.c
1634: ep->hcpriv = NULL;
# vi drivers/usb/host/isp1760-hcd.c +1634
static void isp1760_endpoint_disable(...)
...
ep->hcpriv = NULL;
/* Cannot free qh here since it will be parsed
schedule_ptds() */
schedule_ptds(hcd);
...
Last
reference
overridden
by
Questions