Skip to content

Linux module registry crashes when loading an ELF shared object whose offset-zero PT_LOAD has a non-zero virtual address #1128

Description

@tntljc

Linux module registry crashes when loading an ELF shared object whose offset-zero PT_LOAD has a non-zero virtual address

Description

Frida Gum 17.9.6 may crash when dlopen() loads a valid ELF shared object whose PT_LOAD segment covering file offset zero has a non-zero p_vaddr.

During a dynamic-loader notification, gum_enumerate_modules_using_r_debug() treats link_map::l_addr as the address of the mapped ELF header:

gum_compute_elf_range_from_ehdr (
    (const ElfW(Ehdr) *) lm->l_addr, &range);

However, link_map::l_addr is the ELF load bias. It is only equal to the ELF-header address when the offset-zero PT_LOAD has p_vaddr == 0.

For the layout used by this reproducer:

offset-zero PT_LOAD p_vaddr = 0x400000
mapped ELF header           = l_addr + 0x400000

Gum therefore dereferences an unmapped load-bias address and receives SIGSEGV before emitting the module-added signal.

Environment

  • Frida Gum: 17.9.6
  • OS: glibc-based Linux
  • Reproduced on: x86-64 and AArch64

Reproduction

1. Create a shared library with the relevant ELF layout

Save as fixture.c:

__attribute__ ((visibility ("default")))
int
fixture_add_one (int value)
{
  return value + 1;
}

Build it:

cc -g -fPIC -shared fixture.c \
  -Wl,-Ttext-segment=0x400000 \
  -o libnonzero-vaddr.so

Verify its program headers:

readelf -W -l libnonzero-vaddr.so | grep -m1 LOAD

The output should show file offset zero and virtual address 0x400000, similar to:

LOAD  0x000000  0x0000000000400000  ...

2. Create the Gum reproducer

Save as repro.c:

#define _GNU_SOURCE

#include "frida-gum.h"

#include <dlfcn.h>
#include <link.h>

static gboolean saw_test_module = FALSE;
static GumAddress observed_base = 0;

static void
on_module_added (GumModuleRegistry * registry,
                 GumModule * module,
                 gpointer user_data)
{
  const gchar * path = gum_module_get_path (module);

  if (g_str_has_suffix (path, "libnonzero-vaddr.so"))
  {
    const GumMemoryRange * range = gum_module_get_range (module);

    saw_test_module = TRUE;
    observed_base = range->base_address;

    g_print ("module-added: %s at 0x%" G_GINT64_MODIFIER "x\n",
        path, (guint64) observed_base);
  }
}

int
main (void)
{
  GumModuleRegistry * registry;
  gulong handler;
  void * handle;
  struct link_map * lm = NULL;
  GumAddress expected_base;
  int result;

  gum_init_embedded ();

  registry = gum_module_registry_obtain ();
  handler = g_signal_connect (registry, "module-added",
      G_CALLBACK (on_module_added), NULL);

  /*
   * An affected Gum build receives SIGSEGV during this call, before
   * on_module_added() is invoked.
   */
  handle = dlopen ("./libnonzero-vaddr.so", RTLD_NOW | RTLD_LOCAL);
  if (handle == NULL)
  {
    g_printerr ("dlopen failed: %s\n", dlerror ());
    return 1;
  }

  if (dlinfo (handle, RTLD_DI_LINKMAP, &lm) != 0 || lm == NULL)
  {
    g_printerr ("dlinfo failed: %s\n", dlerror ());
    return 1;
  }

  expected_base = (GumAddress) lm->l_addr + 0x400000;

  g_print ("load bias:     0x%" G_GINT64_MODIFIER "x\n",
      (guint64) lm->l_addr);
  g_print ("expected base: 0x%" G_GINT64_MODIFIER "x\n",
      (guint64) expected_base);
  g_print ("observed base: 0x%" G_GINT64_MODIFIER "x\n",
      (guint64) observed_base);

  result = saw_test_module && observed_base == expected_base ? 0 : 2;

  g_signal_handler_disconnect (registry, handler);
  dlclose (handle);
  gum_deinit_embedded ();

  return result;
}

3. Build and run

cc -O0 -g repro.c -o repro \
  $(pkg-config --cflags --libs frida-gum-1.0) \
  -ldl

./repro

Actual result

The process receives SIGSEGV during dlopen(), before the module-added callback is dispatched.

A symbolized backtrace includes:

gum_compute_elf_range_from_ehdr
gum_enumerate_modules_using_r_debug

Expected result

  • dlopen() succeeds.
  • Gum emits the module-added signal.
  • The reported module base is the mapped offset-zero segment:
link_map::l_addr + 0x400000

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions