|

kctl defines its own kdi_dmods (GCC 14 -fno-common), so the kernel's copy stays NULL and ::findleaks misses kmdb dmods #14

New Chris Tusa opened this issue 40 days ago
Chris Tusa opened this issue 40 days ago

Summary

kmdb keeps its own copy of kdi_dmods, so the kernel's copy stays NULL and
mdb's ::findleaks cannot see kmdb's debugger-module list.

The pointer is meant to be shared: genunix defines it
(uts/common/os/kdi.c:34, declared extern in uts/common/sys/kdi.h:62) and
kctl publishes its dmod list through it
(cmd/mdb/common/kmdb/kctl/kctl_dmod.c:435, kdi_dmods = &kctl_dmods). But
kctl_dmod.c:44 also defines it:

struct modctl		*kdi_dmods;

GCC 14 defaults to -fno-common and the build never passes -fcommon, so that
line is a strong .bss definition rather than a common symbol. kmdbmod
therefore binds its own references to its own copy.

Measured (alpha12, rev 501, -k boot, kmdb active)

genunix  kdi_dmods:           0
kmdbmod  kmdbmod`kdi_dmods:   fffffffffc08dce0     (= &kctl_dmods, 8 dmods loaded)

Symbol table, same build:

kctl_dmod.o  kdi_dmods  OBJT GLOB .bss
kmdbmod      kdi_dmods  OBJT GLOB .bss
genunix      kdi_dmods  OBJT GLOB .bss
gcc -Q --help=common:   -fcommon [disabled]

Impact

Low. Nothing in uts reads kdi_dmods. Its one consumer is mdb's genunix
dmod, cmd/mdb/common/modules/genunix/leaky_subr.c:465:

/* If kmdb is loaded, we need to walk it's module list, since kmdb
 * modctl structures can reference kmem allocations. */
if ((mdb_readvar(&dmods, "kdi_dmods") != -1) && (dmods != 0))
        (void) mdb_pwalk("modctl", (mdb_walk_cb_t)leaky_modctl, NULL, dmods);

With the kernel copy NULL, ::findleaks skips kmdb's modctls, so on a system
with kmdb loaded, allocations referenced only from them can be reported as
leaks. Not measured.

Not caused by the GNU ld conversion: the definition is strong in the object
file before any link. Not checked against upstream illumos.

Fix sketch

Delete the definition at kctl_dmod.c:44 so kctl uses the extern from
<sys/kdi.h> (include it directly if kmdb/kmdb_kdi.h does not already).
kmdbmod then carries kdi_dmods as GLOB UNDEF, bound to genunix's copy by
krtld, both for a -k primary load and for a later module load.

Gate

  1. elfdump -s kmdbmod: kdi_dmods is GLOB UNDEF, and no definition remains.
  2. After a -k boot: echo kdi_dmods/K | mdb -k is non-zero and equals the
    address of kmdbmod's kctl_dmods.
  3. Negative control: the current build reads 0 at step 2.
  4. -k boot still activates kmdb with 0 traps; both boot matrices.

Assignees

No assignees

Labels

No labels

Severity

Low