To set up an interrupt handler for a PCI device, you need to do
something like this:
pci_intr_handle_t ih;
pci_intr_map(pa, &ih);
printf(": %s", pci_intr_string(pa->pa_pc, ih));
pci_intr_establish(pa->pa_pc, ih, ...);
On arm64, pci_intr_handle_t is a pointer to an opaque struct. The
pci_intr_map() function allocates that struct using malloc(9) and in
order not to leak the memory, pci_intr_establish() frees the struct
using free(9). That is all fine, except that some drivers do things
in a slightly different order:
pci_intr_handle_t ih;
pci_intr_map(pa, &ih);
pci_intr_establish(pa->pa_pc, ih, ...);
printf(": %s", pci_intr_string(pa->pa_pc, ih));
That means the call to pci_intr_string() is a use-after-free. The
consequences are rather mild; it prints the wrong string. But this
isn't right.
Now the big question is whether the arm64 implementation needs to be
changed, or whether we should fix the drivers. Below is a fix to
nvme(4) for example.
Thoughts?
Index: dev/pci/nvme_pci.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/nvme_pci.c,v
retrieving revision 1.7
diff -u -p -r1.7 nvme_pci.c
--- dev/pci/nvme_pci.c 10 Jan 2018 15:45:46 -0000 1.7
+++ dev/pci/nvme_pci.c 31 May 2019 20:33:46 -0000
@@ -90,6 +90,7 @@ nvme_pci_attach(struct device *parent, s
struct pci_attach_args *pa = aux;
pcireg_t maptype;
pci_intr_handle_t ih;
+ const char *intrstr;
int msi = 1;
psc->psc_pc = pa->pa_pc;
@@ -113,14 +114,18 @@ nvme_pci_attach(struct device *parent, s
msi = 0;
}
+ intrstr = pci_intr_string(pa->pa_pc, ih);
sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_BIO,
msi ? nvme_intr : nvme_intr_intx, sc, DEVNAME(sc));
if (sc->sc_ih == NULL) {
- printf(": unable to establish interrupt\n");
+ printf(": unable to establish interrupt");
+ if (intrstr != NULL)
+ printf(" at %s", intrstr);
+ printf("\n");
goto unmap;
}
- printf(": %s", pci_intr_string(pa->pa_pc, ih));
+ printf(": %s", intrstr);
if (nvme_attach(sc) != 0) {
/* error printed by nvme_attach() */
goto disestablish;