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 -h
  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
    -p, --pid=PID              Set pid
    -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(10);
    int *val = foo();
    *val = 33;
    bar(val);
    *val = 84;
    bar(val);
    return 0;
  }
  $ gcc doublefree_generator.c
  $ sudo ./doublefree -c a.out
  2024-Jan-25 13:19:32 INFO Execute child process: a.out
  2024-Jan-25 13:19:32 INFO execute command: a.out(pid 108346)
  Tracing doublefree... Hit Ctrl-C to stop
  free(): double free detected in tcache 2

  Allocation:
          iovisor#1 0x0055f7b647f19b foo+0x12
          iovisor#2 0x0055f7b647f1e3 main+0x27
          iovisor#3 0x007f0ae6e29d90

  First deallocation:
          iovisor#1 0x007f0ae6ea53e0 free+0
          iovisor#2 0x0055f7b647f1fd main+0x41
          iovisor#3 0x007f0ae6e29d90

  Second deallocation:
          iovisor#1 0x007f0ae6ea53e0 free+0
          iovisor#2 0x0055f7b647f213 main+0x57
          iovisor#3 0x007f0ae6e29d90

  ^C2024-Jan-25 13:19:32 ERROR Failed to poll perf_buffer
  $
  • Loading branch information
Bojun-Seo committed Jan 25, 2024
1 parent 1a784cf commit 8cd3199
Show file tree
Hide file tree
Showing 5 changed files with 666 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
170 changes: 170 additions & 0 deletions libbpf-tools/doublefree.bpf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/* SPDX-License-Identifier: (LGPL-2.1 OR 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_PERF_EVENT_ARRAY);
__type(key, u32);
__type(value, u32);
} events SEC(".maps");

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);
__type(key, u32);
} stack_traces SEC(".maps");

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

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

if (address == 0)
return 0;

info.stackid = bpf_get_stackid(ctx, &stack_traces, BPF_F_USER_STACK);
info.alloc_count = 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 stackid = 0;
u64 addr = (u64)address;
struct event event = {};
struct doublefree_info_t *info = bpf_map_lookup_elem(&allocs, &addr);

if (!info)
return 0;

__sync_fetch_and_add(&info->alloc_count, -1);
stackid = bpf_get_stackid(ctx, &stack_traces, BPF_F_USER_STACK);
if (info->alloc_count == 0) {
bpf_map_update_elem(&deallocs, &addr, &stackid, BPF_ANY);
} else if (info->alloc_count < 0) {
event.stackid = stackid;
event.addr = addr;
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event,
sizeof(event));
} else {
event.err = -1;
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event,
sizeof(event));
}
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 8cd3199

Please sign in to comment.