module HeapInfo::Glibc

Define errors.

Implement free-related functions.

To define heap-related functions in glibc.

Define some useful functions here.

Attributes

dumper[RW]
main_arena[RW]
size_t[RW]

Public Instance Methods

free(mem)
Alias for: libc_free
libc_free(mem) click to toggle source

Implmentation of +void __libc_free(void *mem)+.

Reference: {github.com/david942j/heapinfo/blob/master/examples/libcdb/libc-2.23/malloc.c#L2934 glibc-2.23} and {code.woboq.org/userspace/glibc/malloc/malloc.c.html#__libc_free Online Source} @param [Integer] mem Memory address to be free.

# File lib/heapinfo/glibc/free.rb, line 10
def libc_free(mem)
  # TODO: free_hook
  mem = ulong mem
  return if mem.zero? # free(0) has no effect
  ptr = mem2chunk(mem)
  return munmap_chunk(ptr) if chunk_is_mmapped(ptr)
  av = arena_for_chunk(ptr)
  int_free(av, ptr)
end
Also aliased as: free
malloc_assert(condition) { || ... } click to toggle source
# File lib/heapinfo/glibc/error.rb, line 8
def malloc_assert(condition)
  raise MallocError, yield unless condition
end

Private Instance Methods

aligned_ok(size) click to toggle source

Not the real implmentation, maybe wrong some day? @return [Boolean]

# File lib/heapinfo/glibc/helper.rb, line 30
def aligned_ok(size)
  (size & (2 * size_t - 1)).zero?
end
arena_for_chunk(ptr) click to toggle source

@return [HeapInfo::Arena]

# File lib/heapinfo/glibc/helper.rb, line 40
def arena_for_chunk(ptr)
  # not support arena other than initial main_arena
  return if dumper.call(ptr, size_t * 2).to_chunk.non_main_arena?
  main_arena
end
chunk_is_mmapped(ptr) click to toggle source

@return [Boolean]

# File lib/heapinfo/glibc/helper.rb, line 11
def chunk_is_mmapped(ptr)
  # TODO: handle memory not accessible
  dumper.call(ptr, size_t * 2).to_chunk.mmapped?
end
fastbin_index(size) click to toggle source

@return [Integer]

# File lib/heapinfo/glibc/helper.rb, line 47
def fastbin_index(size)
  (size >> (size_t == 8 ? 4 : 3)) - 2
end
get_max_fast()
Alias for: max_fast
int_free(av, ptr) click to toggle source

Implmentation of void _int_free (mstate av, mchunkptr p, [int have_lock]).

The original method in C is too long, split to multiple methods to match ruby convention. @param [HeapInfo::Arena] av @param [Integer] ptr Use ptr instead of p to prevent confusing with ruby native method.

# File lib/heapinfo/glibc/free.rb, line 28
def int_free(av, ptr)
  chunk = dumper.call(ptr, size_t * 2).to_chunk
  size = ulong chunk.size
  invalid_pointer(ptr, size)
  invalid_size(size)
  # check_inuse_chunk # off
  if size <= get_max_fast
    int_free_fast(av, ptr, size)
  elsif !chunk_is_mmapped(ptr) # Though this has been checked in #libc_free
    int_free_small(av, ptr, size)
    # else # never reached block, redundant code in glibc.
    # munmap_chunk(ptr)
  end
end
int_free_fast(av, ptr, size) click to toggle source
# File lib/heapinfo/glibc/free.rb, line 43
def int_free_fast(av, ptr, size)
  invalid_next_size(:fast, av, ptr, size)
  idx = fastbin_index(size)
  old = av.fastbin[idx].fd
  malloc_assert(old != ptr) do
    format("double free or corruption (fasttop)\ntop of fastbin[0x%x]: 0x%x=0x%x", size & -8, ptr, ptr)
  end
  true
end
int_free_small(av, ptr, size) click to toggle source
# File lib/heapinfo/glibc/free.rb, line 53
def int_free_small(av, ptr, size) # rubocop:disable UnusedMethodArgument
  # TODO: unfinished
  true
end
invalid_next_size(type, av, ptr, size) click to toggle source
# File lib/heapinfo/glibc/free.rb, line 80
def invalid_next_size(type, av, ptr, size)
  errmsg = "free(): invalid next size (#{type})\n"
  nxt_chk = dumper.call(ptr + size, size_t * 2).to_chunk(base: ptr + size)
  malloc_assert(nxt_chk.size > 2 * size_t) do
    errmsg + format("next chunk(0x%x) has size(#{nxt_chk.size}) <=  2 * #{size_t}", nxt_chk.base)
  end
  malloc_assert(nxt_chk.size < av.system_mem) do
    errmsg + format('next chunk(0x%x) has size(0x%x) >= av.system_mem(0x%x)',
                    nxt_chk.base, nxt_chk.size, av.system_mem)
  end
end
invalid_pointer(ptr, size) click to toggle source

Start of checkers

# File lib/heapinfo/glibc/free.rb, line 65
def invalid_pointer(ptr, size)
  errmsg = "free(): invalid pointer\n"
  # unsigned compare
  malloc_assert(ptr <= ulong(-size)) { errmsg + format('ptr(0x%x) > -size(0x%x)', ptr, ulong(-size)) }
  malloc_assert((ptr % (size_t * 2)).zero?) { errmsg + format('ptr(0x%x) %% %d != 0', ptr, size_t * 2) }
end
invalid_size(size) click to toggle source
# File lib/heapinfo/glibc/free.rb, line 72
def invalid_size(size)
  errmsg = "free(): invalid size\n"
  malloc_assert(size >= min_chunk_size) do
    errmsg + format('size(0x%x) < min_chunk_size(0x%x)', size, min_chunk_size)
  end
  malloc_assert(aligned_ok(size)) { errmsg + format('alignment error: size(0x%x) %% 0x%x != 0', size, size_t * 2) }
end
max_fast() click to toggle source
# File lib/heapinfo/glibc/helper.rb, line 16
def max_fast
  size_t * 16
end
Also aliased as: get_max_fast
mem2chunk(mem) click to toggle source
# File lib/heapinfo/glibc/helper.rb, line 6
def mem2chunk(mem)
  ulong(mem - 2 * size_t)
end
min_chunk_size() click to toggle source

The minimal chunk size. Not the real implmentation, maybe wrong some day? @return [Integer] The size.

# File lib/heapinfo/glibc/helper.rb, line 24
def min_chunk_size
  size_t * 4
end
munmap_chunk(ptr) click to toggle source
# File lib/heapinfo/glibc/free.rb, line 58
def munmap_chunk(ptr) # rubocop:disable UnusedMethodArgument
  # TODO: check page alignment and... page exists?
  true
end
ulong(n) click to toggle source

@return [Integer]

# File lib/heapinfo/glibc/helper.rb, line 35
def ulong(n)
  n % 2**(size_t * 8)
end