NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

port-amd64/60936: interrupt disestablish/establish cycle corrupts evcnts



>Number:         60936
>Category:       port-amd64
>Synopsis:       interrupt disestablish/establish cycle corrupts evcnts
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    port-amd64-maintainer
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Thu Oct 29 06:30:00 +0000 2026
>Originator:     Taylor R Campbell
>Release:        current, 11, 10, ...
>Organization:
The Use-after-BSD Interruption, Inc.
>Environment:
>Description:

	After suspend/resume, I noticed some interrupt-related evcnts
	were missing from `vmstat -ve'.  Iterating over the allevents
	list in gdb shows that the linked list structure is corrupted.

	I suspect this is happening here:

    492 	isp = ci->ci_isources[slot];
    493 	if (isp == NULL) {
    494 		const char *via;
    495 
    496 		isp = chained;
    497 		KASSERT(isp != NULL);
    498 		if (pic->pic_type == PIC_MSI || pic->pic_type == PIC_MSIX)
    499 			via = "vec";
    500 		else
    501 			via = "pin";
    502 		snprintf(isp->is_evname, sizeof (isp->is_evname),
    503 		    "%s %d", via, pin);
    504 		evcnt_attach_dynamic(&isp->is_evcnt, EVCNT_TYPE_INTR, NULL,
    505 		    pic->pic_name, isp->is_evname);
    506 		isp->is_active_cpu = ci->ci_cpuid;
    507 		ci->ci_isources[slot] = isp;
    508 	}

	https://nxr.netbsd.org/xref/src/sys/arch/x86/x86/intr.c?r=1.169#492

	I added a check to evcnt_attach_dynamic to see whether the
	evcnt is already listed:

	TAILQ_FOREACH(ev1, &allevents, ev_list) {
		if (((char *)ev <= (char *)ev1 &&
			(char *)(ev + 1) > (char *)ev1) ||
		    ((char *)ev1 <= (char *)ev &&
			(char *)(ev1 + 1) > (char *)ev)) {
			printf("%s: overwriting %p [%s %s] as %p [%s %s]\n",
			    __func__,
			    ev1, ev1->ev_group, ev1->ev_name, ev, group, name);
			db_stacktrace();
		}
	}

	And sure enough:

[ 20843.030419] evcnt_attach_dynamic: overwriting 0xffff8c8091f6e038 [msix1 vec 0] as 0xffff8c8091f6e038 [msix1 vec 0]
[ 20843.030419] intr_allocate_slot_cpu() at netbsd:intr_allocate_slot_cpu+0x15e
[ 20843.031419] intr_allocate_slot() at netbsd:intr_allocate_slot+0x12a
[ 20843.032419] intr_establish_xname() at netbsd:intr_establish_xname+0x160
[ 20843.033419] x86_pci_msix_establish() at netbsd:x86_pci_msix_establish+0x7e
[ 20843.034419] nvme_pci_intr_establish() at netbsd:nvme_pci_intr_establish+0x133
[ 20843.035419] nvme_resume() at netbsd:nvme_resume+0x3f
[ 20843.036419] nvme_pci_resume() at netbsd:nvme_pci_resume+0x11
[ 20843.037419] device_pmf_driver_resume() at netbsd:device_pmf_driver_resume+0x3d
[ 20843.038419] pmf_device_resume() at netbsd:pmf_device_resume+0x110
[ 20843.039419] pmf_system_resume() at netbsd:pmf_system_resume+0xd9
[ 20843.040419] acpi_enter_sleep_state() at netbsd:acpi_enter_sleep_state+0x14e
[ 20843.041419] sysctl_hw_acpi_sleepstate() at netbsd:sysctl_hw_acpi_sleepstate+0xe8
[ 20843.042419] sysctl_dispatch() at netbsd:sysctl_dispatch+0xbd
[ 20843.043419] sys___sysctl() at netbsd:sys___sysctl+0xec
[ 20843.044419] syscall() at netbsd:syscall+0xaf
[ 20843.044419] --- syscall (number 202) ---

	It's not immediately clear to me how this chained intrsource
	data structure is supposed to work (well, evidently it doesn't
	work!), but it seems that either:

	(a) pci_intr_disestablish -- as used by the nvme(4) suspend
	    handler -- doesn't correctly detach the evcnt when it
	    should (possibly leading to use-after-free!), or

	(b) intr_allocate_slot_cpu needs to recognize when the chained
	    interrupt source is already attached and not issue another
	    evcnt_attach_dynamic.

>How-To-Repeat:

	suspend/resume a laptop with nvme

>Fix:

	Yes, please!




Home | Main Index | Thread Index | Old Index