Bug 297187 - `bcm2835_audio_release()` Uses a Freed VCHI Service After Close
Summary: `bcm2835_audio_release()` Uses a Freed VCHI Service After Close
Status: In Progress
Alias: None
Product: Base System
Classification: Unclassified
Component: kern (show other bugs)
Version: 16.0-CURRENT
Hardware: Any Any
: --- Affects Some People
Assignee: Christos Margiolis
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2026-07-31 07:20 UTC by xuqing yang
Modified: 2026-08-12 12:52 UTC (History)
2 users (show)

See Also:


Attachments
patch for the uaf bug (479 bytes, patch)
2026-07-31 13:06 UTC, xuqing yang
no flags Details | Diff
patch for the uaf bug (503 bytes, patch)
2026-08-05 12:22 UTC, Mark Linimon
no flags Details | Diff
improved patch (4.51 KB, patch)
2026-08-12 11:25 UTC, Christos Margiolis
no flags Details | Diff
uaf patch (368 bytes, patch)
2026-08-12 11:28 UTC, Christos Margiolis
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:20:59 UTC
## Summary

`bcm2835_audio_release()` calls `vchi_service_close()` and then unconditionally calls `vchi_service_release()` with the same service handle.

In the VCHI shim implementation, a successful `vchi_service_close()` calls `service_free(service)`. The subsequent `vchi_service_release()` therefore dereferences a freed `SHIM_SERVICE_T` object when it reads `service->handle`.

This is a real kernel heap-use-after-free during `bcm2835_audio` device detach. It can result in a kernel panic or other undefined kernel behavior.

I have not identified a remote network trigger. I am reporting this privately because it is a kernel use-after-free and I would appreciate the Security Team's classification.

## Bug Details

### Vulnerable caller

At `sys/arm/broadcom/bcm2835/bcm2835_audio.c:358`, `bcm2835_audio_release()` checks the service handle. At lines 363-367 it closes the service and then releases the same handle:

```c
if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
        success = vchi_service_close(sc->vchi_handle);
        if (success != 0)
                BCM2835_LOG_ERROR(sc, "vchi_service_close failed: %d\\n",
                    success);
        vchi_service_release(sc->vchi_handle);
        sc->vchi_handle = VCHIQ_SERVICE_HANDLE_INVALID;
}
```

### Free in `vchi_service_close()`

At `sys/contrib/vchiq/interface/vchiq_arm/vchiq_shim.c:694-708`, a successful close frees the service object:

```c
int32_t vchi_service_close(const VCHI_SERVICE_HANDLE_T handle)
{
        int32_t ret = -1;
        SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle;
        if (service) {
                VCHIQ_STATUS_T status = vchiq_close_service(service->handle);
                if (status == VCHIQ_SUCCESS) {
                        service_free(service);
                        service = NULL;
                }

                ret = vchiq_status_to_vchi(status);
        }
        return ret;
}
```

`service_free()` at approximately `sys/contrib/vchiq/interface/vchiq_arm/vchiq_shim.c:625` deletes the queue and frees the `SHIM_SERVICE_T` object.

### Use after free in `vchi_service_release()`

At `sys/contrib/vchiq/interface/vchiq_arm/vchiq_shim.c:851-860`, `vchi_service_release()` casts the handle and reads `service->handle`:

```c
int32_t vchi_service_release(const VCHI_SERVICE_HANDLE_T handle)
{
        int32_t ret = -1;
        SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle;
        if (service)
                ret = vchiq_status_to_vchi(
                    vchiq_release_service(service->handle));
        return ret;
}
```

When `vchi_service_close()` returns success, `service` points to freed memory before `vchi_service_release()` is called. The read of `service->handle` is consequently a use-after-free.

The vulnerable call chain is:

```text
bcm2835_audio_detach()
  -> bcm2835_audio_release()                     [bcm2835_audio.c:358]
    -> vchi_service_close(sc->vchi_handle)       [bcm2835_audio.c:363]
      -> vchiq_close_service(service->handle)    [vchiq_shim.c:699]
      -> service_free(service)                   [vchiq_shim.c:701]
    -> vchi_service_release(sc->vchi_handle)    [bcm2835_audio.c:367]
      -> vchiq_release_service(service->handle)  [vchiq_shim.c:856]
```

## Impact and Severity

The direct impact is a kernel heap-use-after-free during audio device detach. Depending on allocator state and the contents of the freed object, this may cause a kernel panic, failed driver unload, or other undefined kernel behavior.

Preliminary assessment:

- **Confidentiality:** No impact observed.
- **Integrity:** No impact observed.
- **Availability:** Possible local kernel panic or denial of service.
- **Privilege requirement:** Device detach or driver unload likely requires root or equivalent administrative privilege.
- **Remote exploitability:** Not demonstrated and unlikely through network traffic.
- **Security classification:** Likely a local kernel reliability issue rather than a remotely exploitable DoS under the stated FreeBSD policy. It may be suitable for a normal bug report or an Errata Notice rather than a Security Advisory.

The use-after-free is nevertheless in kernel code and could warrant security handling during initial triage.

## Proposed Fix

The minimal fix is to avoid calling `vchi_service_release()` after a successful `vchi_service_close()`, because the close operation has already freed the shim service object:

```diff
--- a/sys/arm/broadcom/bcm2835/bcm2835_audio.c
+++ b/sys/arm/broadcom/bcm2835/bcm2835_audio.c
@@ -360,12 +360,12 @@ bcm2835_audio_release(struct bcm2835_audio_info *sc)
 	if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
 		success = vchi_service_close(sc->vchi_handle);
 		if (success != 0)
 			BCM2835_LOG_ERROR(sc, "vchi_service_close failed: %d\\n",
 			    success);
-		vchi_service_release(sc->vchi_handle);
 		sc->vchi_handle = VCHIQ_SERVICE_HANDLE_INVALID;
 	}
```

The final fix should confirm the intended VCHI lifecycle semantics for a failed `vchi_service_close()`. If `vchi_service_release()` is required only after a failed close, the implementation should call it conditionally and only while the handle remains valid. It should never be called after a successful close has freed the shim service.

Reporter: Xuqing Yang
Comment 1 Mark Linimon freebsd_committer freebsd_triage 2026-07-31 08:18:56 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:06:39 UTC
Created attachment 273354 [details]
patch for the uaf bug
Comment 3 Mark Linimon freebsd_committer freebsd_triage 2026-08-05 12:22:03 UTC
Created attachment 273481 [details]
patch for the uaf bug

^Triage: convert to git diff format (now preferred).
Comment 4 Christos Margiolis freebsd_committer freebsd_triage 2026-08-12 11:25:49 UTC
Created attachment 273678 [details]
improved patch

I improved the patch. The handling in uaudio_attach() is actually useless because audio_rev is fetched in uaudio_mixer_fill_info() only. I just swapped the udev argument for uaa. Can you test this on the "main" branch? Otherwise you'll need to modify it a bit.
Comment 5 Christos Margiolis freebsd_committer freebsd_triage 2026-08-12 11:28:54 UTC
Created attachment 273680 [details]
uaf patch

Oops. My last comment and patch was meant for 294803...
Comment 6 Christos Margiolis freebsd_committer freebsd_triage 2026-08-12 11:37:03 UTC
Mark, the patch and the analysis looks good to me. vchi_service_close() casts "handle" to "service":

SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle;

And then if vchiq_close_service() succeeds, it frees "service", i.e., handle. The fix seems correct even though I don't have the HW to test it.
Comment 7 Mark Johnston freebsd_committer freebsd_triage 2026-08-12 12:52:14 UTC
(In reply to Christos Margiolis from comment #6)
The patch fixes the UAF, yes, but that vchi_release_service() releases a reference which might block vchi_service_close() from completing successfully.  So I'd maybe also add a /* XXX vchi_service_release()? */ comment above the vchi_service_close() call.