WARNING in integrity_commit

Status: moderation: reported on 2026/11/09 18:30
Subsystems: dm
Labels: prio:low
[Documentation on labels]
Reported-by: syzbot+c1edda4bc38263ea2c30@syzkaller.appspotmail.com
First crash: 1h15m, last: 1h15m
✨ AI-generated root cause summary:

In drivers/md/dm-integrity.c, journal accounting variables (including ic->free_sectors, ic->free_section, and section counts) are initialized exclusively within replay_journal() rather than during target allocation in dm_integrity_ctr(). When a dm-integrity target configured in journal mode ('J') is resumed, dm_integrity_resume() checks whether the on-disk superblock was modified while suspended; if superblock_changed() evaluates to true, it logs an error and jumps to skip_writes, completely bypassing replay_journal(). When the device is subsequently suspended or receives a flush bio, integrity_commit() is scheduled and calls pad_uncommitted(), which validates that total journal sections match the sum of uncommitted, committed, and free sectors; because replay_journal() was skipped, these counters remain zero, causing the invariant check to fail and trigger the WARN_ON.

▶ ▼ ✨ Full AI root cause analysis
Based on kernel commit 7475ea8b5861:

Technical Root Cause Analysis

1. Components and Architecture

  • Source File: drivers/md/dm-integrity.c
  • Structures and Fields:
    • struct dm_integrity_c:
      • journal_sections: Total number of journal sections configured on the device.
      • journal_section_entries: Number of journal entries per section.
      • journal_entries: Total entries across all sections (journal_sections * journal_section_entries).
      • n_uncommitted_sections: Number of journal sections containing uncommitted entries.
      • n_committed_sections: Number of committed journal sections awaiting writeback to data areas.
      • free_sectors: Number of free entries/slots currently available in the journal.
      • free_section, free_section_entry, uncommitted_section, committed_section.
      • mode: Target operating mode ('J' for Journal, 'B' for Bitmap, 'D' for Direct, 'R' for Recovery, 'I' for Inline).
  • Key Functions:
    • dm_integrity_ctr()
    • dm_integrity_resume()
    • replay_journal()
    • integrity_commit()
    • pad_uncommitted()
    • dm_integrity_postsuspend() / submit_flush_bio()

2. Defect Mechanism and Lifecycle Invariant Violation

In drivers/md/dm-integrity.c, the journal state machine maintains an accounting invariant across all journal entries: $$\text{journal_sections} \times \text{journal_section_entries} = (n_uncommitted_sections + n_committed_sections) \times \text{journal_section_entries} + free_sectors$$

During target construction in dm_integrity_ctr(), struct dm_integrity_c *ic is allocated with kzalloc_obj(), leaving all fields zeroed. calculate_device_limits() calculates ic->journal_sections, ic->journal_section_entries, and ic->journal_entries. However, the journal position and capacity accounting fields (free_sectors, free_section, uncommitted_section, committed_section, n_committed_sections, n_uncommitted_sections) are not initialized in dm_integrity_ctr(). Instead, their initialization is deferred to replay_journal():

static void replay_journal(struct dm_integrity_c *ic)
{
    ...
    ic->committed_section = continue_section;
    ic->n_committed_sections = 0;

    ic->uncommitted_section = continue_section;
    ic->n_uncommitted_sections = 0;

    ic->free_section = continue_section;
    ic->free_section_entry = 0;
    ic->free_sectors = ic->journal_entries;
    ...
}

When the device mapper target is resumed via dm_integrity_resume(), the driver re-reads the superblock from the underlying device with sync_rw_sb(ic, REQ_OP_READ) and checks whether any immutable geometry parameters were changed behind its back via superblock_changed(ic):

    if (unlikely(superblock_changed(ic))) {
        memcpy(ic->sb, ic->sb_copy, sizeof(struct superblock));
        DMERR("The superblock was changed while the device was suspended");
        dm_integrity_io_error(ic, "superblock check", -EINVAL);
        goto skip_writes;
    }

If superblock_changed(ic) returns true (e.g., if the backing block device was modified between target creation and resume):

  1. dm_integrity_io_error() records the error in ic->failed.
  2. Execution jumps directly to skip_writes:, which bypasses replay_journal(ic).
  3. Consequently, ic->free_sectors, ic->n_uncommitted_sections, and ic->n_committed_sections are never initialized and remain 0.

3. How the Failure Manifests

Even though the device is marked as failed, work items can still be queued to ic->commit_wq:

  • When the target is subsequently suspended or torn down by userspace, dm_integrity_postsuspend() runs and unconditionally queues commit work:
    queue_work(ic->commit_wq, &ic->commit_work);
    drain_workqueue(ic->commit_wq);
    
  • Alternatively, if a flush request arrives (REQ_PREFLUSH), dm_integrity_map() dispatches it via submit_flush_bio(), which also invokes queue_work(ic->commit_wq, &ic->commit_work).

When the worker thread executes integrity_commit() on ic->commit_wq:

  1. For journal mode (ic->mode == 'J'), it calls pad_uncommitted(ic).
  2. pad_uncommitted() evaluates the journal entry invariant:
    if (WARN_ON(ic->journal_sections * ic->journal_section_entries !=
            (ic->n_uncommitted_sections + ic->n_committed_sections) *
            ic->journal_section_entries + ic->free_sectors))
    
  3. Because replay_journal() was skipped, the right-hand side evaluates to $(0 + 0) \times \text{journal_section_entries} + 0 = 0$, whereas the left-hand side is positive ($\text{journal_sections} \times \text{journal_section_entries} > 0$).
  4. The invariant check fails, triggering the reported WARN_ON at drivers/md/dm-integrity.c:2885.
▶ ▼ ✨ AI Jobs (1)
ID Workflow Result Correct Bug Created Started Finished Revision
35ef7f84 assessment-security DenialOfService: ❌ Exploitable: ❌ FilesystemTrigger: ❌ FuzzingArtifact: ❌ MemoryCorruption: ❌ NetworkNeedsSetup: ❌ NetworkTrigger: ❌ PeripheralTrigger: ❌ RemoteTrigger: ❌ Unprivileged: ❌ UserNamespace: ❌ VMGuestTrigger: ❌ VMHostTrigger: ❌ ❓ WARNING in integrity_commit 2026/11/09 17:35 2026/11/09 17:36 2026/11/09 18:18 5cecaf1f

Sample crash report:
------------[ cut here ]------------
ic->journal_sections * ic->journal_section_entries != (ic->n_uncommitted_sections + ic->n_committed_sections) * ic->journal_section_entries + ic->free_sectors
WARNING: drivers/md/dm-integrity.c:2885 at pad_uncommitted drivers/md/dm-integrity.c:2885 [inline], CPU#2: kworker/2:1/64
WARNING: drivers/md/dm-integrity.c:2885 at integrity_commit.cold+0x6/0x75 drivers/md/dm-integrity.c:2917, CPU#2: kworker/2:1/64
Modules linked in:
CPU: 2 UID: 0 PID: 64 Comm: kworker/2:1 Tainted: G             L      syzkaller #0 PREEMPT(full) 
Tainted: [L]=SOFTLOCKUP
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Workqueue: dm-integrity-commit integrity_commit
RIP: 0010:pad_uncommitted drivers/md/dm-integrity.c:2885 [inline]
RIP: 0010:integrity_commit.cold+0x6/0x75 drivers/md/dm-integrity.c:2917
Code: ff e9 4d b2 63 07 48 8b 7c 24 58 e8 a1 b3 23 01 e9 e7 fe ff ff 48 8b 7c 24 40 e8 92 b3 23 01 e9 21 ff ff ff e8 78 48 a9 00 90 <0f> 0b 90 41 54 8b 8c 24 d0 00 00 00 89 da 44 89 f6 41 89 d9 45 89
RSP: 0018:ffffc90000d27a68 EFLAGS: 00010093
RAX: 0000000000000000 RBX: 0000000000000050 RCX: 0000000000000001
RDX: ffff8880212bcb00 RSI: ffffffff81653888 RDI: 0000000000000004
RBP: 0000000000000050 R08: 0000000000000050 R09: 0000000000000000
R10: 0000000000000003 R11: 000000000000776b R12: 0000000000000000
R13: 0000000000000000 R14: 0000000000000001 R15: 1ffff920001a4f6a
FS:  0000000000000000(0000) GS:ffff8880d4671000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007fd7a3d25ff8 CR3: 000000005b7c4000 CR4: 0000000000352ef0
Call Trace:
 <TASK>
 process_one_work+0xac7/0x1b10 kernel/workqueue.c:3495
 process_scheduled_works kernel/workqueue.c:3578 [inline]
 worker_thread+0x5ef/0xe50 kernel/workqueue.c:3659
 kthread+0x373/0x450 kernel/kthread.c:436
 ret_from_fork+0x730/0xd60 arch/x86/kernel/process.c:158
 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
 </TASK>

Crashes (1):
Time Kernel Commit Syzkaller Config Log Report Syz repro C repro VM info Assets (help?) Manager Title
2026/11/09 17:35 upstream 7475ea8b5861 bf7e1705 .config console log report info [disk image (non-bootable)] [vmlinux] [kernel image] ci-qemu-upstream WARNING in integrity_commit
* Struck through repros no longer work on HEAD.