Bug 297186 - `mvneta` Attach Failure Causes Kernel Use-After-Free/Double-Free
Summary: `mvneta` Attach Failure Causes Kernel Use-After-Free/Double-Free
Status: New
Alias: None
Product: Base System
Classification: Unclassified
Component: kern (show other bugs)
Version: 16.0-CURRENT
Hardware: Any Any
: --- Affects Some People
Assignee: freebsd-bugs (Nobody)
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2026-07-31 07:15 UTC by xuqing yang
Modified: 2026-08-01 20:35 UTC (History)
0 users

See Also:


Attachments
patch for the uaf bug (158 bytes, patch)
2026-07-31 13:05 UTC, xuqing yang
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description xuqing yang 2026-07-31 07:15:29 UTC
## Summary

When `mvneta_dma_create()` fails after one or more DMA tags have been created, 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 second 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 privately 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-514`:

   ```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 = mvneta_dma_create(sc);
   if (error != 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 != NULL)
           bus_dma_tag_destroy(sc->tx_dtag);
   if (sc->rx_dtag != NULL)
           bus_dma_tag_destroy(sc->rx_dtag);
   if (sc->txmbuf_dtag != NULL)
           bus_dma_tag_destroy(sc->txmbuf_dtag);
   if (sc->rxbuf_dtag != 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:170` dispatches to the tag-specific implementation:

   ```c
   tc = (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 != 0) {
           error = EBUSY;
           goto out;
   }
   if (dmat->segments != 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` object 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 cleanup path. I have not demonstrated a remote network trigger; the condition appears 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 triggerable 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 = mvneta_dma_create(sc);
if (error != 0) {
        mvneta_detach(self);
        return (error);
}
```

As additional defensive hardening, `mvneta_detach()` could clear every pointer 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 != NULL)
 		bus_dma_tag_destroy(sc->tx_dtag);
+	sc->tx_dtag = NULL;
 	if (sc->rx_dtag != NULL)
 		bus_dma_tag_destroy(sc->rx_dtag);
+	sc->rx_dtag = NULL;
 	if (sc->txmbuf_dtag != NULL)
 		bus_dma_tag_destroy(sc->txmbuf_dtag);
+	sc->txmbuf_dtag = NULL;
 	if (sc->rxbuf_dtag != NULL)
 		bus_dma_tag_destroy(sc->rxbuf_dtag);
+	sc->rxbuf_dtag = 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
Comment 1 Mark Linimon freebsd_committer freebsd_triage 2026-07-31 08:19:58 UTC
^Triage: note that this Problem Report contains an inline patch.

(These days, we prefer that patches be submitted as Attachments.)
Comment 2 xuqing yang 2026-07-31 13:05:40 UTC
Created attachment 273353 [details]
patch for the uaf bug