# Collected Facts — ODEBUG WARNING in lane_ioctl

## Debug symbols

- **VMLINUX**: `oops-workdir/syzbot/vmlinux-e728258d`  
  Downloaded from syzbot storage; decompressed with `unxz`.  
  `nm` loaded 402 014 text symbols successfully.
- **SOURCEDIR**: `oops-workdir/linux` — checked out to `e728258debd5`  
  (net-next tree, merge tag `net-7.1-rc1`)
- Semcode index was refreshed to HEAD_COMMIT before collection.

## Source tree

- Path: `/sdb1/arjan/git/linux-kernel-oops/oops-workdir/linux`
- HEAD commit: `e728258debd5`
- Key source files used:
  - `lib/debugobjects.c`
  - `kernel/time/timer.c`
  - `net/atm/lec.c`
  - `net/atm/ioctl.c`

## Resolved addresses

All non-inline backtrace entries resolved via `backtrace_resolve.py`:

| Index | Function | Address | Source (primary) | Inlined frames |
|-------|----------|---------|-----------------|----------------|
| 0 | `__debug_object_init` | `0xffffffff84bc2d2f (0xffffffff84bc2a20 + 0x30f)` | `lib/debugobjects.c` (addr2line returned line 0; source_hint: line 780) | `debug_print_object` (addr2line primary) |
| 1 | `timer_init_key` | `0xffffffff81b1ce61 (0xffffffff81b1ce20 + 0x41)` | `kernel/time/timer.c:788` (debug_timer_init) | `debug_init@836`, `timer_init_key@880` |
| 2 | `lane_ioctl` | `0xffffffff8ae1a809 (0xffffffff8ae19290 + 0x1579)` | `net/atm/lec.c` (addr2line returned line 0; source_hint: 1037) | `lec_arp_init`, `lecd_attach`, `lane_ioctl` (all line 0) |
| 3 | `do_vcc_ioctl` | `0xffffffff8ae003dd (0xffffffff8ae00070 + 0x36d)` | `net/atm/ioctl.c` (addr2line returned line 0; source_hint: 159) | none |
| 4 | `svc_ioctl` | `0xffffffff8adfe8c6 (0xffffffff8adfe6d0 + 0x1f6)` | `net/atm/svc.c:611` | none |
| 5 | `sock_do_ioctl` | `0xffffffff897a71a1 (0xffffffff897a70a0 + 0x101)` | `net/socket.c` line 0 (source_hint: 1313) | none |
| 6 | `sock_ioctl` | `0xffffffff897a5b06 (0xffffffff897a5540 + 0x5c6)` | `net/socket.c:1434` | none |
| 7 | `__se_sys_ioctl` | `0xffffffff824860cc (0xffffffff82485fd0 + 0xfc)` | `fs/ioctl.c:51` (vfs_ioctl) | `__do_sys_ioctl@597`, `__se_sys_ioctl@583` |
| 8 | `do_syscall_64` | `0xffffffff8bb861df (0xffffffff8bb86080 + 0x15f)` | `arch/x86/entry/syscall_64.c:63` (do_syscall_x64) | `do_syscall_64@94` |
| 9 | `entry_SYSCALL_64_after_hwframe` | `0xffffffff81000130 (0xffffffff810000b9 + 0x77)` | `arch/x86/entry/entry_64.S:121` | none |

Note: addr2line returned line 0 for entries 0, 2, 3, 5 — DWARF incomplete at those addresses. Format 3 syzbot annotations used as source locations.

## Code bytes — disasm around crash RIP

The crash instruction is a `call` to `__SCT__WARN_trap` at `0xffffffff84bc2d2f`:

```
ffffffff84bc2cf3:  call   ffffffff82310c20 <__asan_report_load8_noabort>
ffffffff84bc2cf8:  mov    (%r12),%r12
ffffffff84bc2cfc:  mov    %r13,%rax
ffffffff84bc2cff:  shr    $0x3,%rax
ffffffff84bc2d03:  cmpb   $0x0,(%rax,%r15,1)
ffffffff84bc2d08:  je     ffffffff84bc2d12 <__debug_object_init+0x2f2>
ffffffff84bc2d0a:  mov    %r13,%rdi
ffffffff84bc2d0d:  call   ffffffff82310c20 <__asan_report_load8_noabort>
ffffffff84bc2d12:  mov    0x0(%r13),%r9
ffffffff84bc2d16:  mov    %r14,%rdi
ffffffff84bc2d19:  mov    $0xffffffff8c28a720,%rsi
ffffffff84bc2d20:  mov    %r12,%rdx
ffffffff84bc2d23:  mov    0x8(%rsp),%ecx
ffffffff84bc2d27:  mov    (%rsp),%r8
ffffffff84bc2d2b:  push   0x10(%rsp)
ffffffff84bc2d2f:  call   ffffffff8bbbde98 <__SCT__WARN_trap>   <<< crash
ffffffff84bc2d34:  add    $0x8,%rsp
ffffffff84bc2d38:  incl   0xb778c8e(%rip)   # debug_objects_warnings counter
ffffffff84bc2d3e:  cmp    $0x3,%ebp
ffffffff84bc2d41:  jne    ffffffff84bc2d82 <__debug_object_init+0x362>
ffffffff84bc2d43:  mov    0x18(%rsp),%r14
```

## Register annotations

| Register | Value | Decoded meaning |
|----------|-------|----------------|
| RBX | `0xffff888077a60f78` | Address of the `timer_list` object being re-initialized (kernel heap, slab) |
| R08 | `0xffff888077a60f78` | Same as RBX — both point to the same `timer_list` object |
| RBP | `0x0000000000000003` | `ODEBUG_STATE_ACTIVE` = 3 — the object state at the time of the WARN |
| R10 | `0xdffffc0000000000` | KASAN shadow base (standard) |
| R15 | `0xdffffc0000000000` | KASAN shadow base (standard) |
| CR2 | `0x000055a602110fd8` | User-space address — last page fault prior to this crash, unrelated |

## Full source excerpts

### `lec_arp_init` — net/atm/lec.c:1264

```c
1264 static void lec_arp_init(struct lec_priv *priv)
1265 {
1266     unsigned short i;
1267 
1268     for (i = 0; i < LEC_ARP_TABLE_SIZE; i++)
1269         INIT_HLIST_HEAD(&priv->lec_arp_tables[i]);
1270     INIT_HLIST_HEAD(&priv->lec_arp_empty_ones);
1271     INIT_HLIST_HEAD(&priv->lec_no_forward);
1272     INIT_HLIST_HEAD(&priv->mcast_fwds);
1273     spin_lock_init(&priv->lec_arp_lock);
1274     INIT_DELAYED_WORK(&priv->lec_arp_work, lec_arp_check_expire);
1275     schedule_delayed_work(&priv->lec_arp_work, LEC_ARP_REFRESH_INTERVAL);
1276 }
```

`INIT_DELAYED_WORK` expands to `timer_setup` → `timer_init_key`, which
calls `debug_object_init` on the embedded `timer_list` inside `lec_arp_work`.

### `lecd_attach` path — net/atm/lec.c:748

```c
748 static int lecd_attach(struct atm_vcc *vcc, int arg)
749 {
    ...
759     if (!dev_lec[i]) {
        // new device: alloc + register
775         priv = netdev_priv(dev_lec[i]);
776     } else {
777         priv = netdev_priv(dev_lec[i]);
778         if (rcu_access_pointer(priv->lecd))
779             return -EADDRINUSE;
780     }
781     lec_arp_init(priv);   // called for both new and existing priv
```

`lec_arp_init` is called unconditionally on line 781, regardless of whether
`priv` is freshly allocated or already exists (else-branch, lines 776-779).

### Recent commits to `net/atm/lec.c` at HEAD

```
922814879542 atm: lec: fix use-after-free in sock_def_readable()
101bacb303e8 atm: lec: fix null-ptr-deref in lec_arp_clear_vccs
bf4afc53b77a Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
69050f8d6d07 treewide: Replace kmalloc with kmalloc_obj for non-scalar types
d03b79f459c7 net: atm: fix /proc/net/atm/lec handling
d13a3824bfd2 net: atm: add lec_mutex
```

The `d13a3824bfd2` commit (`net: atm: add lec_mutex`) added `mutex_lock(&lec_mutex)` in `lane_ioctl` and added `lockdep_assert_held(&lec_mutex)` in `lecd_attach`. That commit's commit message references a different syzbot bug (KASAN slab-use-after-free). The `lec_arp_init` call on line 781 predates that commit.

### `__debug_object_init` — state switch

```c
767     switch (obj->state) {
768     case ODEBUG_STATE_NONE:
769     case ODEBUG_STATE_INIT:
770     case ODEBUG_STATE_INACTIVE:
771         obj->state = ODEBUG_STATE_INIT;
772         raw_spin_unlock_irqrestore(&db->lock, flags);
773         return;
774     default:
775         break;
776     }
778     o = *obj;
779     raw_spin_unlock_irqrestore(&db->lock, flags);
780     debug_print_object(&o, "init");   // fires when state != NONE/INIT/INACTIVE
```

The oops message says `active state 0` — the `astate` field is 0 and the object
`state` is `ODEBUG_STATE_ACTIVE` (= 3, matching RBP = 3).

## Factual notes

- The ODEBUG message: `"ODEBUG: init active (active state 0) object: ffff888077a60f78 object type: timer_list hint: lec_arp_check_expire+0x0/0xc90 net/atm/lec.c:-1"` — the hint is the `debug_hint` callback of `timer_debug_descr`; it resolves the timer function pointer to `lec_arp_check_expire`, which is the work function passed to `INIT_DELAYED_WORK` at `net/atm/lec.c:1274`.
- The `hint: ... net/atm/lec.c:-1` suffix is the `%pS` printk specifier resolving the function symbol; `-1` is a compiler artefact in the offset for a function pointer at offset 0.
- `timer_init_key` calls `debug_init` → `debug_timer_init` → `debug_object_init` → `__debug_object_init`. The full inline chain is confirmed by both the Format 3 syzbot annotations and `addr2line` output from `backtrace_resolve.py`.
- `lec_arp_init` also calls `schedule_delayed_work` immediately after `INIT_DELAYED_WORK`. A second call to `lec_arp_init` on an already-active work would call `INIT_DELAYED_WORK` (and thus `timer_init_key`) on a timer that may be in the active state from the earlier `schedule_delayed_work`.
- `lecd_attach` at line 781 calls `lec_arp_init(priv)` for the else-branch (existing `dev_lec[i]`, NULL `lecd`) without first cancelling `priv->lec_arp_work`.
- The `svc_ioctl` function at `net/atm/svc.c:611` falls through to `vcc_ioctl` → `do_vcc_ioctl` for commands not handled by `svc_ioctl` itself.
- `do_vcc_ioctl` iterates `ioctl_list` under `ioctl_mutex`; `lane_ioctl` is registered on that list via `lane_ioctl_ops`.
- `lockdep_assert_held(&lec_mutex)` is present in `lecd_attach` (line 753). The `lec_mutex` is taken in `lane_ioctl` (line 1034) before `lecd_attach` is called.
