[Bug 297186] `mvneta` Attach Failure Causes Kernel Use-After-Free/Double-Free

[email protected] Fri, 31 Jul 2026 07:15:29 +0000
Newsgroups gmane.os.freebsd.bugs
Message-ID <[email protected]/bugzilla/>
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=3D297186

            Bug ID: 297186
           Summary: `mvneta` Attach Failure Causes Kernel
                    Use-After-Free/Double-Free
           Product: Base System
           Version: CURRENT
          Hardware: Any
                OS: Any
            Status: New
          Severity: Affects Some People
          Priority: ---
         Component: kern
          Assignee: [email protected]
          Reporter: [email protected]

## Summary

When `mvneta_dma_create()` fails after one or more DMA tags have been creat=
ed,
it calls `mvneta_detach()` internally. The caller, `mvneta_attach()`, then
treats the failure as a normal attach failure and calls `mvneta_detach()` a
second time.

`mvneta_detach()` destroys the DMA tags and frees the network interface, but
does not clear the corresponding pointers in `struct mvneta_softc`. The sec=
ond
cleanup therefore operates on stale pointers and can cause a use-after-free,
double destruction/free of a DMA tag, or a double free/use-after-free of the
`ifnet` object.

I have not identified a remote network trigger. I am reporting this private=
ly
because the defect is a kernel use-after-free/double-free and I would
appreciate the Security Team's classification.

## Bug Details

1. `mvneta_dma_create()` starts creating DMA resources at
`sys/dev/neta/if_mvneta.c:409`.

2. After `rxbuf_dtag` has been successfully created, allocation or mapping =
of
the first RX ring can fail. For example, the failure can occur when
`bus_dmamem_alloc()` or `bus_dmamap_load()` returns an error.

3. The failure reaches the cleanup label at `sys/dev/neta/if_mvneta.c:513-5=
14`:

   ```c
   fail:
           mvneta_detach(sc->dev);
           return (error);
   ```

4. `mvneta_attach()` calls `mvneta_dma_create()` at
`sys/dev/neta/if_mvneta.c:668`. If it returns an error, the caller invokes
`mvneta_detach()` again at `sys/dev/neta/if_mvneta.c:670`:

   ```c
   error =3D mvneta_dma_create(sc);
   if (error !=3D 0) {
           mvneta_detach(self);
           return (error);
   }
   ```

5. During the first detach, `sys/dev/neta/if_mvneta.c:850-858` destroys the=
 DMA
tags:

   ```c
   if (sc->tx_dtag !=3D NULL)
           bus_dma_tag_destroy(sc->tx_dtag);
   if (sc->rx_dtag !=3D NULL)
           bus_dma_tag_destroy(sc->rx_dtag);
   if (sc->txmbuf_dtag !=3D NULL)
           bus_dma_tag_destroy(sc->txmbuf_dtag);
   if (sc->rxbuf_dtag !=3D NULL)
           bus_dma_tag_destroy(sc->rxbuf_dtag);
   ```

   The pointers are not set to `NULL` after destruction.

6. On arm64, `bus_dma_tag_destroy()` at `sys/arm64/arm64/busdma_machdep.c:1=
70`
dispatches to the tag-specific implementation:

   ```c
   tc =3D (struct bus_dma_tag_common *)dmat;
   return (tc->impl->tag_destroy(dmat));
   ```

7. `bounce_bus_dma_tag_destroy()` starts at
`sys/arm64/arm64/busdma_bounce.c:329`. It reads `dmat->map_count` at
approximately line 334 and frees the tag at approximately line 340 when the=
 map
count is zero:

   ```c
   if (dmat->map_count !=3D 0) {
           error =3D EBUSY;
           goto out;
   }
   if (dmat->segments !=3D NULL)
           free(dmat->segments, M_DEVBUF);
   free(dmat, M_DEVBUF);
   ```

8. The second `mvneta_detach()` passes the stale `rxbuf_dtag` pointer to
`bus_dma_tag_destroy()`. This causes a read from freed memory and may
subsequently cause a second free.

The same cleanup problem affects the other DMA tag fields. In addition,
`mvneta_detach()` frees `sc->ifp` at `sys/dev/neta/if_mvneta.c:863` without
clearing `sc->ifp`, so the second detach may also free the same `ifnet` obj=
ect
again.

## Impact and Severity

The direct impact is a kernel panic or failed driver initialization when the
DMA allocation or mapping failure occurs.

No evidence of confidentiality or integrity impact has been found. Normal
packet reception and transmission do not appear to reach the vulnerable cle=
anup
path. I have not demonstrated a remote network trigger; the condition appea=
rs
to require a DMA allocation/mapping failure during device attachment,
potentially caused by local administrative action, hardware state, resource
exhaustion, or fault injection.

Preliminary assessment:

- **Confidentiality:** No impact observed.
- **Integrity:** No impact observed.
- **Availability:** Possible local kernel panic or failed driver
initialization.
- **Remote exploitability:** Not demonstrated; likely not remotely triggera=
ble
through network traffic.
- **FreeBSD policy classification:** Likely a local reliability/kernel
stability issue rather than a remotely exploitable DoS. Please classify it
according to the Security Team's policy.

## Proposed Fix

The simplest fix is to make `mvneta_attach()` the sole owner of cleanup when
`mvneta_dma_create()` fails:

```diff
--- a/sys/dev/neta/if_mvneta.c
+++ b/sys/dev/neta/if_mvneta.c
@@ -510,8 +510,6 @@ mvneta_dma_create(struct mvneta_softc *sc)
        }

 fail:
-       mvneta_detach(sc->dev);
-
        return (error);
 }
```

With this change, `mvneta_attach()` performs the single cleanup:

```c
error =3D mvneta_dma_create(sc);
if (error !=3D 0) {
        mvneta_detach(self);
        return (error);
}
```

As additional defensive hardening, `mvneta_detach()` could clear every poin=
ter
immediately after successful destruction/freeing:

```diff
--- a/sys/dev/neta/if_mvneta.c
+++ b/sys/dev/neta/if_mvneta.c
@@ -849,15 +849,27 @@ mvneta_detach(device_t dev)
        if (sc->tx_dtag !=3D NULL)
                bus_dma_tag_destroy(sc->tx_dtag);
+       sc->tx_dtag =3D NULL;
        if (sc->rx_dtag !=3D NULL)
                bus_dma_tag_destroy(sc->rx_dtag);
+       sc->rx_dtag =3D NULL;
        if (sc->txmbuf_dtag !=3D NULL)
                bus_dma_tag_destroy(sc->txmbuf_dtag);
+       sc->txmbuf_dtag =3D NULL;
        if (sc->rxbuf_dtag !=3D NULL)
                bus_dma_tag_destroy(sc->rxbuf_dtag);
+       sc->rxbuf_dtag =3D NULL;
```

Pointer clearing alone may not be sufficient because the detach routine also
releases resources and frees `sc->ifp`. The preferred fix is to avoid
performing cleanup twice and to define a single owner for partial-attach
cleanup. Any final patch should also be tested against all attach failure
paths.

Reporter: Xuqing Yang

--=20
You are receiving this mail because:
You are the assignee for the bug.=