Skip to contentSkip to conversation

insmod bug causes kernel unwind failures for module text

Linux-ia64 Dave Anderson 3 participants 3 messages Jul 25, 2002 – Jul 30, 2002

lore
Share & tools
lore
#0 lore raw

There is a bug in the initialization of the unw_table_entry structures for all kernel modules, such that any unwind operation that comes upon a kernel module text address will fail to find its associated unwind info data in the build_script() routine. (Actually it won't be able to determine what module it belongs to.)

The reason that the unwind fails is due to the faulty initialization of each module's unw_table structure, specifically the start and end fields. For example, here's a typical module unw_table structure, in which the "start" field is less than its "segment_base", and the "end" field is not even in the module's address space:

  crash> unw_table 000000116cd5008
  struct unw_table {
    next = 0xe000000116cd51e8,
    name = 0xa000000000150b30 "keybdev",
    gp = 0xa000000000150ed8,
    segment_base = 0xa0000000001500c0,
    start = 0xa000000000150000,
    end = 0xa00000000014f740,
    array = 0xa000000000150a50,
    length = 0x7
  }

The build_script() routine can never find a module in which the target IP fits between start and end, so it bails out thinking it's come upon a leaf routine.

As it turns out, the real problem is due to the contents of the array of unw_table_entry structures, the first and last of which are used to calculate the start and end fields:

static void
init_unwind_table (struct unw_table *table, const char *name,
unsigned long segment_base, unsigned long gp, const void *table_start, const void *table_end)

  {
          const struct unw_table_entry *start = table_start, *end table_end;

          table->name = name;
          table->segment_base = segment_base;
          table->gp = gp;
==>      table->start = segment_base + start[0].start_offset;
==>      table->end = segment_base + end[-1].end_offset;
          table->array = start;
          table->length = end - start;

}

The "start[0].start_offset" and "end[-1].end_offset" values contain invalid negative numbers. Using the keybdev.o example above, here's what its 7 unw_table_entry structures look like:

  crash> unw_table_entry 0xa000000000150a50 7
  struct unw_table_entry {
    start_offset = 0xffffffffffffff40,
    end_offset = 0xfffffffffffffcb0,
    info_offset = 0xfffffffffffff680
  }
  struct unw_table_entry {
    start_offset = 0xfffffffffffffca0,
    end_offset = 0xfffffffffffffb60,
    info_offset = 0xfffffffffffff660
  }
  struct unw_table_entry {
    start_offset = 0xfffffffffffffb60,
    end_offset = 0xfffffffffffffa40,
    info_offset = 0xfffffffffffff640
  }
  struct unw_table_entry {
    start_offset = 0xfffffffffffffa40,
    end_offset = 0xfffffffffffff890,
    info_offset = 0xfffffffffffff620
  }
  struct unw_table_entry {
    start_offset = 0xfffffffffffff880,
    end_offset = 0xfffffffffffff810,
    info_offset = 0xfffffffffffff600
  }
  struct unw_table_entry {
    start_offset = 0xfffffffffffff800,
    end_offset = 0xfffffffffffff6f0,
    info_offset = 0xfffffffffffff5e8
  }
  struct unw_table_entry {
    start_offset = 0xfffffffffffff6e0,
    end_offset = 0xfffffffffffff680,
    info_offset = 0xfffffffffffff5c8
  }

The faulty negative numbers are incorrectly calculated and initialized by insmod. David Mosberger pointed me in the proper location, where there is a bug in obj/obj_ia64.c, in the arch_apply_relocation() routine, as it applies to .IA_64.unwind relocations:

    case R_IA64_SEGREL32LSB :   /* @segrel(sym + add), data4 LSB */
    case R_IA64_SEGREL64LSB :   /* @segrel(sym + add), data8 LSB */
        if (targsec->header.sh_type & SHT_NOBITS)
            v = ifile->bss - v;
        else if (targsec->header.sh_flags & SHF_EXECINSTR)
            v = ifile->text - v;
        else
            v = ifile->data - v;
        if (r_info = R_IA64_SEGREL32LSB)
            COPY_32LSB(loc, v);
        else
            COPY_64LSB(loc, v);
        break;

In the case of .IA_64.unwind section data, each reallocation operation mistakenly falls into the default path above where it does the "v = ifile->data - v;" calculation.

What it should do, in the case of unw_table_entry data, is shown in the patch below, which assigns offset values that the kernel init_unwind_table() expects to find:

obj_ia64.c Thu Jul 25 12:22:08 2002 renamed from obj_ia64.c.orig Thu Jul 25 12:31:58 2002 +2 -0
@@ -915,6 +915,8 @@
            v = ifile->bss - v;
        else if (targsec->header.sh_flags & SHF_EXECINSTR)
            v = ifile->text - v;
+       else if (targsec->header.sh_type = SHT_IA_64_UNWIND)
+           v = v - ifile->text;
        else
            v = ifile->data - v;
        if (r_info = R_IA64_SEGREL32LSB)

although it doesn't address David M's questioning the SHF_EXECINSTR calculation as well -- or the others for that matter. Should those be flipped as well?

Dave Anderson

#1 lore raw
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line · Dave Anderson

Dave> There is a bug in the initialization of the unw_table_entry Dave> structures for all kernel modules, such that any unwind Dave> operation that comes upon a kernel module text address will Dave> fail to find its associated unwind info data in the Dave> build_script() routine. (Actually it won't be able to Dave> determine what module it belongs to.)

Has this been resolved?

I don't recall seeing any followups.

	--david