⬢ DragonFlyBSD Kernel Audit
DF-3654 / harness.c
← back to finding ↓ download raw
/*
 * DF-3654 harness — lock-pairing decision table for handle_hpd_rx_irq()
 * (sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm.c:1195-1255).
 *
 *   amdgpu_dm.c:1209-1210   if (dc_link->type != dc_connection_mst_branch)
 *                               mutex_lock(&aconnector->hpd_lock);   [ENTRY]
 *   amdgpu_dm.c:1232         dc_link_detect(dc_link, DETECT_REASON_HPDRX);
 *                               ^ can flip link->type:
 *                                 single -> mst_branch  (dc_link.c:528)
 *                                 mst_branch -> single   (dc_link.c:569,
 *                                   dm_helpers_dp_mst_start_top_mgr failure)
 *   amdgpu_dm.c:1251-1254   if (dc_link->type != dc_connection_mst_branch) {
 *                               drm_dp_cec_irq(...);
 *                               mutex_unlock(&aconnector->hpd_lock);  [EXIT]
 *                           }
 *
 * The lock/unlock decisions are made against DIFFERENT reads of the mutable
 * link->type.  This harness enumerates the entry x exit matrix exactly as the
 * kernel computes it and reports which combinations strand the lock (permanent
 * hang of HPD work, dm_suspend/dm_resume, amdgpu_dm_irq_fini's flush_work)
 * or drop a lock that was never taken (lockmgr LK_RELEASE corruption/panic).
 */
#include <stdio.h>

enum link_type { TYPE_SINGLE = 0, TYPE_MST };

static const char *tname[] = { "single", "mst_branch" };

int main(void)
{
	int entry, exit_type, bad = 0;

	printf("handle_hpd_rx_irq() hpd_lock pairing matrix "
	       "(amdgpu_dm.c:1209 x 1251):\n");
	for (entry = 0; entry <= 1; entry++) {
		for (exit_type = 0; exit_type <= 1; exit_type++) {
			int locked   = (entry != TYPE_MST);     /* amdgpu_dm.c:1209 */
			int unlocked = (exit_type != TYPE_MST); /* amdgpu_dm.c:1251 */
			printf("  entry=%-11s exit=%-11s lock=%d unlock=%d : ",
			       tname[entry], tname[exit_type], locked, unlocked);
			if (locked && !unlocked) {
				printf("STRANDED LOCK\n");
				bad++;
			} else if (!locked && unlocked) {
				printf("UNLOCK WITHOUT HOLD\n");
				bad++;
			} else {
				printf("balanced\n");
			}
		}
	}
	printf("\n%d unbalanced combinations; both flips occur INSIDE the handler:\n",
	       bad);
	printf("  single    -> mst_branch : dc_link.c:528 (dc_link_detect discovers branch)\n");
	printf("  mst_branch-> single     : dc_link.c:569 (start_top_mgr fails, sink-controlled)\n");
	printf("\nConsequences of a stranded hpd_lock (entry=single, exit=mst_branch):\n");
	printf("  - handle_hpd_irq() (amdgpu_dm.c:1083) blocks forever in its work item\n");
	printf("  - dm_resume()/dm_suspend() block forever (amdgpu_dm.c:864,878)\n");
	printf("  - amdgpu_dm_irq_fini() flush_work() hangs (amdgpu_dm_irq.c:334)\n");
	return bad == 0;
}