Skip to content

Commit

Permalink
libbpf-tools: Add new feature doublefree
Browse files Browse the repository at this point in the history
Add doublefree tool to detect double free. It could detect user level double
free error currently and can be expanded to detect kernel level double free
error. Followings are the usage and example.

Usage:

  $ ./doublefree --help
  Usage: doublefree [OPTION...]
  Detect and report double free error.

  -c or -p is a mandatory option
  EXAMPLES:
      doublefree -p 1234             # Detect doublefree on process id 1234
      doublefree -c a.out            # Detect doublefree on a.out
      doublefree -c 'a.out arg'      # Detect doublefree on a.out with argument
      doublefree -c "a.out arg"    # Detect doublefree on a.out with argument

    -c, --command=COMMAND      Execute and trace the specified command
    -i, --interval=INTERVAL    Set interval in second to detect leak
    -p, --pid=PID              Set pid
    -T, --top=TOP              Report only specified amount of backtraces
    -v, --verbose              Verbose debug output
    -?, --help                 Give this help list
        --usage                Give a short usage message
    -V, --version              Print program version

  Mandatory or optional arguments to long options are also mandatory or optional
  for any corresponding short options.

  Report bugs to https://github.com/iovisor/bcc/tree/master/libbpf-tools.

Example:

  $ cat doublefree_generator.c
  \#include <unistd.h>
  \#include <stdlib.h>

  int* foo() {
    return (int*)malloc(sizeof(int));
  }

  void bar(int* p) {
    free(p);
  }

  int main(int argc, char* argv[]) {
    sleep(50);
    int *val = foo();
    *val = 33;
    bar(val);
    *val = 84;
    bar(val);
    return 0;
  }

  $ gcc doublefree_generator.c
  $ ./a.out &
  [1] 5718
  $ sudo ./doublefree -p 5718
  2023-Dec-21 10:29:01 WARN Is this process alive? pid: 5718

  iovisor#1 Found double free...
  Allocation happended on stack_id: 19655
          iovisor#1 0x0000557abf0824 foo+0x10 (/home/bojun/test/doublefree_generator/a.out+0x824)
          iovisor#2 0x0000557abf0868 main+0x1c (/home/bojun/test/doublefree_generator/a.out+0x868)
          iovisor#3 0x00007f990b7780 (/usr/lib/aarch64-linux-gnu/libc.so.6+0x27780)
          iovisor#4 0x00007f990b7858 __libc_start_main+0x98 (/usr/lib/aarch64-linux-gnu/libc.so.6+0x27858)
          iovisor#5 0x0000557abf0730 _start+0x30 (/home/bojun/test/doublefree_generator/a.out+0x730)

  First deallocation happended on stack_id: 52798
          iovisor#1 0x00007f9911f614 free+0 (/usr/lib/aarch64-linux-gnu/libc.so.6+0x8f614)
          iovisor#2 0x0000557abf0880 main+0x34 (/home/bojun/test/doublefree_generator/a.out+0x880)
          iovisor#3 0x00007f990b7780 (/usr/lib/aarch64-linux-gnu/libc.so.6+0x27780)
          iovisor#4 0x00007f990b7858 __libc_start_main+0x98 (/usr/lib/aarch64-linux-gnu/libc.so.6+0x27858)
          iovisor#5 0x0000557abf0730 _start+0x30 (/home/bojun/test/doublefree_generator/a.out+0x730)

  Second deallocation happended on stack_id: 14228
          iovisor#1 0x00007f9911f614 free+0 (/usr/lib/aarch64-linux-gnu/libc.so.6+0x8f614)
          iovisor#2 0x0000557abf0894 main+0x48 (/home/bojun/test/doublefree_generator/a.out+0x894)
          iovisor#3 0x00007f990b7780 (/usr/lib/aarch64-linux-gnu/libc.so.6+0x27780)
          iovisor#4 0x00007f990b7858 __libc_start_main+0x98 (/usr/lib/aarch64-linux-gnu/libc.so.6+0x27858)
          iovisor#5 0x0000557abf0730 _start+0x30 (/home/bojun/test/doublefree_generator/a.out+0x730)
  • Loading branch information
Bojun-Seo committed Dec 21, 2023
1 parent 3469bf1 commit 9c959d4
Show file tree
Hide file tree
Showing 5 changed files with 735 additions and 0 deletions.
1 change: 1 addition & 0 deletions libbpf-tools/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
/capable
/cpudist
/cpufreq
/doublefree
/drsnoop
/execsnoop
/exitsnoop
Expand Down
1 change: 1 addition & 0 deletions libbpf-tools/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ APPS = \
capable \
cpudist \
cpufreq \
doublefree \
drsnoop \
execsnoop \
exitsnoop \
Expand Down
165 changes: 165 additions & 0 deletions libbpf-tools/doublefree.bpf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/* Copyright (c) 2022 LG Electronics */
#include <vmlinux.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
#include "doublefree.h"

struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, MAX_ENTRIES);
__type(key, u64);
__type(value, struct doublefree_info_t);
} allocs SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, MAX_ENTRIES);
__type(key, u64);
__type(value, u64);
} memptrs SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_STACK_TRACE);
__uint(max_entries, MAX_ENTRIES);
__uint(key_size, sizeof(u32));
} stack_traces SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, MAX_ENTRIES);
__type(key, u64);
__type(value, u32);
} first_deallocs SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 10);
__type(key, u64);
__type(value, u32);
} second_deallocs SEC(".maps");

int gen_alloc_exit2(struct pt_regs *ctx, u64 address)
{
struct doublefree_info_t info = {0, };

if (address != 0) {
info.stack_id = bpf_get_stackid(ctx, &stack_traces,
BPF_F_USER_STACK);
info.is_available = 1;
bpf_map_update_elem(&allocs, &address, &info, BPF_ANY);
}
return 0;
}

int gen_alloc_exit(struct pt_regs *ctx)
{
return gen_alloc_exit2(ctx, PT_REGS_RC(ctx));
}

int gen_free_enter(struct pt_regs *ctx, void *address)
{
int stack_id = 0;
u64 addr = (u64)address;
struct doublefree_info_t *info = bpf_map_lookup_elem(&allocs, &addr);

if (!info)
return 0;

info->is_available -= 1;
stack_id = bpf_get_stackid(ctx, &stack_traces, BPF_F_USER_STACK);
if (info->is_available == 0) {
bpf_map_update_elem(&first_deallocs, &addr, &stack_id, BPF_ANY);
} else if (info->is_available < 0) {
bpf_map_update_elem(&second_deallocs, &addr, &stack_id, BPF_ANY);
} else {
bpf_printk("This code should not be printed at any rate\n");
bpf_printk("Please check if something goes wrong\n");
}
return 0;
}

SEC("kretprobe/dummy_malloc")
int BPF_KRETPROBE(malloc_return)
{
return gen_alloc_exit(ctx);
}

SEC("kprobe/dummy_free")
int BPF_KPROBE(free_entry, void *address)
{
return gen_free_enter(ctx, address);
}

SEC("kretprobe/dummy_calloc")
int BPF_KRETPROBE(calloc_return)
{
return gen_alloc_exit(ctx);
}

SEC("kretprobe/dummy_realloc")
int BPF_KRETPROBE(realloc_return)
{
return gen_alloc_exit(ctx);
}

SEC("kprobe/dummy_posix_memalign")
int BPF_KPROBE(posix_memalign_entry, void **memptr, size_t alignment, size_t size)
{
u64 memptr64 = (u64)(size_t)memptr;
u64 pid = bpf_get_current_pid_tgid();

bpf_map_update_elem(&memptrs, &pid, &memptr64, BPF_ANY);
return 0;
}

SEC("kretprobe/dummy_posix_memalign")
int BPF_KRETPROBE(posix_memalign_return)
{
void *addr = NULL;
u64 pid = bpf_get_current_pid_tgid();
u64 *memptr64 = bpf_map_lookup_elem(&memptrs, &pid);

if (memptr64 == NULL)
return 0;

bpf_map_delete_elem(&memptrs, &pid);
if (bpf_probe_read_user(&addr, sizeof(void *), (void *)(size_t)*memptr64))
return 0;

u64 addr64 = (u64)(size_t)addr;
return gen_alloc_exit2(ctx, addr64);
}

SEC("kretprobe/dummy_aligned_alloc")
int BPF_KRETPROBE(aligned_alloc_return)
{
return gen_alloc_exit(ctx);
}

SEC("kretprobe/dummy_valloc")
int BPF_KRETPROBE(valloc_return)
{
return gen_alloc_exit(ctx);
}

SEC("kretprobe/dummy_memalign")
int BPF_KRETPROBE(memalign_return)
{
return gen_alloc_exit(ctx);
}

SEC("kretprobe/dummy_pvalloc")
int BPF_KRETPROBE(pvalloc_return)
{
return gen_alloc_exit(ctx);
}

SEC("kretprobe/dummy_reallocarray")
int BPF_KRETPROBE(reallocarray_return)
{
return gen_alloc_exit(ctx);
}

char _license[] SEC("license") = "GPL";
Loading

0 comments on commit 9c959d4

Please sign in to comment.