linuxkpi: a layer device's kobject is initialised and named dev_name() #18

Merged
toasterson merged 2 commits from x230-lkpi-device-kobject into main 2026-10-05 19:07:16 +00:00
Owner

struct device embeds a kobject that the layer never initialised: its name was NULL and its reference count zero. Linux's device_initialize() and dev_set_name() leave it initialised and named, and drivers rely on that. i915 hangs kobjects under the DRM minor's device (on illumos the PCI device, lkpi_drm_set_kdev()) and reads its name.

On a ThinkPad X230 (HD 4000, 8086:0166), once the display device existed (x230-nodisplay-device), the attach reached i915_driver_register() and panicked:

WARNING at linuxkpi/src/lkpi_kobject.c:148: kobject_get: ... not initialised   (twice)
BAD TRAP: type=e (#pf Page fault) rp=fffffe00104d0650 addr=0 occurred in module "unix" due to a NULL pointer dereference
strncmp+0x20(0, fffffffff41f9873, 2)
is_object_gt+0x1d()
intel_sysfs_rps_init+0x22()
intel_gt_sysfs_pm_init+0x14()
intel_gt_sysfs_register+0xd5()
intel_gt_driver_register+0x40()
i915_driver_register+0xb1()
i915_driver_probe+0x189()
i915_pci_probe+0x8a()
lkpi_pci_attach+0x10e()
i915n_attach+0x17()

lkpi_device_init() now initialises the kobject with a ktype that releases nothing (the device's lifetime stays lkpi_refs) and names it dev_name() (0000:00:02.0 for PCI devices); lkpi_device_fini() drops that reference and kobject_release() frees the name.

Tested on the X230 (branch x230-test): attach completes, lkpi_warnings 0, /dev/dri/card0 and renderD128 exist; detach (update_drv -d), unload of i915n, drm_helpers, linuxkpi and drm_core, and a second attach in the same boot all work.

Review round 1 fix (e68810f)

The first version let lkpi_device_fini() drop its reference while a child kobject still pointed at the device (i915 v7.2 never puts the engine directory that intel_engines_add_sysfs() creates under the device), so the device memory was freed under that child and the name leaked. Now:

  • A layer kobject tracks its children (kobject_add() links, kobject_del() unlinks, under one leaf mutex; no allocation, other lock or release while it is held). lkpi_device_fini() orphans remaining children (their parent becomes NULL and the reference each held is dropped), then drops the device's own reference, which frees the name; it warns about extra references and about an over-put.
  • kobject_add() under a parent that was never initialised is -EINVAL.
  • struct kobject grew, so LINUXKPI_ABI is 2: linuxkpi, i915n and linuxkpi_selftest must be installed together (a mismatch is refused at _init).
  • Tests: host tests linuxkpi/tests/host/t_kobject.c (35 tests, 1138 checks, clang ASan/UBSan and gcc on oibuild), including concurrent add/delete and orphaning racing deletes and puts; st_probe in linuxkpi_selftest checks the device kobject's name and child references (not yet run in a guest).
  • Builds on oibuild: linuxkpi 35/0, linuxkpi_selftest, i915n 157/157 TUs linked with -z defs.

The X230 run above was at 1b86938; the orphaning path has not run on hardware or in a guest yet.

`struct device` embeds a kobject that the layer never initialised: its name was NULL and its reference count zero. Linux's `device_initialize()` and `dev_set_name()` leave it initialised and named, and drivers rely on that. i915 hangs kobjects under the DRM minor's device (on illumos the PCI device, `lkpi_drm_set_kdev()`) and reads its name. On a ThinkPad X230 (HD 4000, `8086:0166`), once the display device existed (x230-nodisplay-device), the attach reached `i915_driver_register()` and panicked: ``` WARNING at linuxkpi/src/lkpi_kobject.c:148: kobject_get: ... not initialised (twice) BAD TRAP: type=e (#pf Page fault) rp=fffffe00104d0650 addr=0 occurred in module "unix" due to a NULL pointer dereference strncmp+0x20(0, fffffffff41f9873, 2) is_object_gt+0x1d() intel_sysfs_rps_init+0x22() intel_gt_sysfs_pm_init+0x14() intel_gt_sysfs_register+0xd5() intel_gt_driver_register+0x40() i915_driver_register+0xb1() i915_driver_probe+0x189() i915_pci_probe+0x8a() lkpi_pci_attach+0x10e() i915n_attach+0x17() ``` `lkpi_device_init()` now initialises the kobject with a ktype that releases nothing (the device's lifetime stays `lkpi_refs`) and names it `dev_name()` (`0000:00:02.0` for PCI devices); `lkpi_device_fini()` drops that reference and `kobject_release()` frees the name. **Tested on the X230** (branch `x230-test`): attach completes, `lkpi_warnings` 0, `/dev/dri/card0` and `renderD128` exist; detach (`update_drv -d`), unload of i915n, drm_helpers, linuxkpi and drm_core, and a second attach in the same boot all work. ## Review round 1 fix (`e68810f`) The first version let `lkpi_device_fini()` drop its reference while a child kobject still pointed at the device (i915 v7.2 never puts the `engine` directory that `intel_engines_add_sysfs()` creates under the device), so the device memory was freed under that child and the name leaked. Now: - A layer kobject tracks its children (`kobject_add()` links, `kobject_del()` unlinks, under one leaf mutex; no allocation, other lock or release while it is held). `lkpi_device_fini()` orphans remaining children (their parent becomes NULL and the reference each held is dropped), then drops the device's own reference, which frees the name; it warns about extra references and about an over-put. - `kobject_add()` under a parent that was never initialised is `-EINVAL`. - `struct kobject` grew, so `LINUXKPI_ABI` is 2: linuxkpi, i915n and linuxkpi_selftest must be installed together (a mismatch is refused at `_init`). - Tests: host tests `linuxkpi/tests/host/t_kobject.c` (35 tests, 1138 checks, clang ASan/UBSan and gcc on oibuild), including concurrent add/delete and orphaning racing deletes and puts; `st_probe` in `linuxkpi_selftest` checks the device kobject's name and child references (not yet run in a guest). - Builds on oibuild: linuxkpi 35/0, linuxkpi_selftest, i915n 157/157 TUs linked with `-z defs`. The X230 run above was at `1b86938`; the orphaning path has not run on hardware or in a guest yet.
linuxkpi: a layer device's kobject is initialised and named dev_name()
Some checks failed
ci / check-hosted (pull_request) Has been cancelled
ci / kmod-illumos (pull_request) Has been cancelled
1b869380a7
struct device embeds a kobject that the layer never initialised: its
name was NULL and its reference count zero. Linux's device_initialize()
and dev_set_name() leave it initialised and named, and drivers rely on
that. i915 hangs kobjects under the DRM minor's device (on illumos the
PCI device, lkpi_drm_set_kdev()) and reads its name. At the first
attach on a ThinkPad X230 (HD 4000, 8086:0166) that was:

  WARNING at linuxkpi/src/lkpi_kobject.c:148: kobject_get: ... not initialised  (twice)
  BAD TRAP: type=e (#pf Page fault) addr=0 occurred in module "unix"
  due to a NULL pointer dereference
  strncmp+0x20(0, ..., 2)
  is_object_gt+0x1d
  intel_sysfs_rps_init+0x22
  intel_gt_sysfs_pm_init+0x14
  intel_gt_sysfs_register+0xd5
  intel_gt_driver_register+0x40
  i915_driver_register+0xb1
  i915_driver_probe+0x189

lkpi_device_init() now initialises the kobject with a ktype that
releases nothing (the device's lifetime stays lkpi_refs) and names it
dev_name() ("0000:00:02.0" for PCI devices); lkpi_device_fini() drops
that reference, and kobject_release() frees the name. A kobject still
holding the device's as its parent keeps it until it lets go.
linuxkpi: orphan the kobjects left under a layer device at its release
Some checks failed
ci / kmod-illumos (pull_request) Failing after 26m17s
ci / check-hosted (pull_request) Successful in 2h18m24s
e68810fa7e
The previous commit initialised a layer device's kobject and dropped its
reference in lkpi_device_fini(), claiming that a kobject still holding the
device's as its parent keeps it alive. It does not: the device's memory
(the struct pci_dev of the PCI glue) is freed right after
lkpi_device_fini() whatever the kobject's count. A child left under it
kept a pointer to freed memory, which its later kobject_del()/last put
dereferences, and the device kobject's name was leaked. That is every
i915n detach: Linux i915's intel_engines_add_sysfs() adds an "engine"
kobject under the DRM minor's device that is never put
(i915_teardown_sysfs() puts only "gt").

An added kobject is now also on its parent's list of children
(lkpi_children, lkpi_sibling in struct kobject; LINUXKPI_ABI 2), linked
by kobject_add() and unlinked by kobject_del(), under one leaf mutex.
lkpi_kobject_orphan_children() unlinks the children of a kobject, sets
their parent to NULL and drops the reference each held on it.
lkpi_device_fini() calls it, then drops the reference kobject_init()
took, which is now the last one (the name is freed); a reference still
left, or a kobject released before the device, is warned about. The
children themselves stay the driver's (i915's "engine" kobjects are
leaked as they were).

A child's reference on its parent is dropped under the lock unless it is
the last one, so once the orphaning returns no concurrent kobject_del()
can still touch the device's kobject and the device may be freed.
kobject_add() under an uninitialised parent now fails with -EINVAL
instead of taking a reference on it.

Tests: host tests of the links, the device's release with leftover
children (a later put of a leftover touches no freed memory under
AddressSanitizer; without the orphaning it does), the warnings, and
concurrent add/del and orphaning against deleting threads.
linuxkpi_selftest checks that the device's kobject is named dev_name(),
that a child takes and its put returns a reference, and leaves one child
under the device whose orphaning is checked at the next probe or _fini.
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
illumos/illumos-drm!18
No description provided.