Re: [PATCH v4] ocfs2/dlm: serialize migration MLE cleanup on failure

From: Joseph Qi

Date: Thu Oct 29 2026 - 23:27:17 EST




On 10/27/26 6:14 PM, Cen Zhang wrote:
> A migration MLE has a base reference for the mastering answer and an
> in-use reference held by dlm_migrate_lockres(). The finishing assertion
> consumes the base reference, but migration failure drops it again
> without checking whether the assertion already consumed it.
>
> The target queues its migration worker before it acknowledges the
> ALL_DONE packet, so its finishing assertion can be handled while the
> sender still waits for the status. If the connection fails before the
> status arrives, dlm_send_one_lockres() returns an error for a migration
> that the target has already completed:
>
> Old-master sender Finishing assertion
> ----------------- -------------------
> publish MLE; take in-use reference
> send ALL_DONE; wait for status
> take lookup reference
> unlink MLE
> put lookup and base references
> send returns a connection error
> put base reference again; free MLE
> put in-use reference; access freed MLE
>
> dlm_assert_master_handler() holds dlm->spinlock from the MLE lookup to
> the final puts, and it takes dlm->master_lock around the unlink and the
> puts. Decide under the same locks: if the MLE is already unhashed, the
> assertion consumed the base reference, so drop only the sender's in-use
> reference. Otherwise unlink it before consuming the base reference so a
> later assertion no longer finds it.
>
> An assertion that got there first has also made the target the owner
> and cleared DLM_LOCK_RES_MIGRATING. Treating the attempt as failed at
> that point would leave the lockres owned by the target while this node
> still holds the other nodes' locks and refmap bits, so it could neither
> be purged nor migrated again. Finish our side as a completed migration
> does instead: drop those locks under res->spinlock if the target still
> owns the lockres, and recalculate its usage.
>
> Use the same cleanup for the pre-send failure, the send failure and the
> target-death failure paths. The sender's in-use reference keeps the MLE
> alive until its final put outside the nested locks. Successful
> migration retains its existing in-use-only put.
>
> A target that has died cannot assert after the sender has seen its
> death. The o2net node-down callback runs before the DLM one; it
> disconnects the node and flushes the workqueue that runs the message
> handlers, so an assertion already received has been handled by the time
> the DLM removes the node from the domain map. A node that leaves the
> domain cleanly sends its exit message only after its assertions have
> been answered.
>
> Two windows of the hand-over itself are left as they are: the handler
> switches the owner and the sender drops the other nodes' locks moments
> later, and an assertion from a live target that arrives after the
> sender has given up still meets the owner check in the handler. Waiting
> for the target before giving up would only stretch the first window
> into the time in which recovery of the target can observe it.
>
> KASAN report, only the relevant frames are kept:
>
> BUG: KASAN: slab-use-after-free in dlm_put_mle_inuse+0xce/0xe0
> Read of size 8 at addr ffff8881190e3c20 by task umount/593
>
> Call Trace:
> <TASK>
> dlm_put_mle_inuse+0xce/0xe0
> dlm_migrate_lockres+0x16af/0x20d0
> dlm_empty_lockres+0x60a/0xbd0
> dlm_unregister_domain+0x53b/0x11d0
> o2cb_cluster_disconnect+0xa5/0x110
> ocfs2_cluster_disconnect+0x73/0xc0
> ocfs2_dlm_shutdown+0x311/0x440
> ocfs2_dismount_volume+0x696/0x940
> generic_shutdown_super+0x15f/0x490
> kill_block_super+0x40/0xa0
> deactivate_locked_super+0xae/0x120
> deactivate_super+0xa2/0xc0
> cleanup_mnt+0x217/0x450
> task_work_run+0x144/0x230
> </TASK>
>
> Allocated by task 593:
> kmem_cache_alloc_noprof+0x20b/0x5c0
> dlm_migrate_lockres+0x20e/0x20d0
> dlm_empty_lockres+0x60a/0xbd0
> dlm_unregister_domain+0x53b/0x11d0
> o2cb_cluster_disconnect+0xa5/0x110
> ocfs2_cluster_disconnect+0x73/0xc0
> ocfs2_dlm_shutdown+0x311/0x440
> ocfs2_dismount_volume+0x696/0x940
>
> Freed by task 593:
> kmem_cache_free+0x242/0x580
> __dlm_put_mle+0x4b2/0x660
> dlm_migrate_lockres+0x1690/0x20d0
> dlm_empty_lockres+0x60a/0xbd0
> dlm_unregister_domain+0x53b/0x11d0
> o2cb_cluster_disconnect+0xa5/0x110
> ocfs2_cluster_disconnect+0x73/0xc0
> ocfs2_dlm_shutdown+0x311/0x440
>
> The buggy address belongs to the object at ffff8881190e3c00
> which belongs to the cache o2dlm_mle of size 480
> The buggy address is located 32 bytes inside of
> freed 480-byte region [ffff8881190e3c00, ffff8881190e3de0)
>
> Fixes: 6714d8e86bf4 ("[PATCH] OCFS2: The Second Oracle Cluster Filesystem")
> Cc: stable@xxxxxxxxxxxxxxx
> Assisted-by: LLM
> Signed-off-by: Cen Zhang <zzzccc427@xxxxxxxxx>

Looks fine.
Reviewed-by: Joseph Qi <joseph.qi@xxxxxxxxxxxxxxxxx>