microsoft / microsoft/ebpf-for-windows
BugCheck in net_ebpf_extension_sock_ops_flow_delete: use-after-free on filter_context (regression from #5200)
Nobody has claimed this yet.
- Dominant language
- C
- Stars
- 3.6k
- Forks
- 311
- Avg merge
- 6d 10h
- Merged PRs (30d)
- 21
Description
## Summary
\
et_ebpf_extension_sock_ops_flow_delete\ crashes with BugCheck 0xBE (ATTEMPTED_WRITE_TO_READONLY_MEMORY) when the WFP flow deletion callback fires on a filter context that has already been freed.
## Crash Analysis
**BugCheck**: 0xBE at \
t!KeAcquireSpinLockRaiseToDpc\ — attempting to acquire a spinlock on a stale pointer pointing into kernel text (read-only code page).
**Stack trace** (from CI run [26119306451](https://github.com/microsoft/ebpf-for-windows/actions/runs/26119306451)):
\\\
NetEbpfExt!net_ebpf_extension_sock_ops_flow_delete+0xe6 [net_ebpf_ext_sock_ops.c @ 632]
NETIO!WfpNotifyFlowContextDelete+0x217
NETIO!KfdAleNotifyFlowDeletion+0x1e2
tcpip!WfpAleFreeRemoteEndpoint+0x20
tcpip!WfpAleDecrementWaitRef+0x84
tcpip!LruCleanupDpcRoutine+0x4ee
\\\
**Key registers/locals at crash frame**:
- \ilter_context = 0xfffff805'542447c0\ — **kernel text address**, not a valid pool allocation
- \ilter_context->lock = 0x08390002'9dda0d8b\ — garbage (reading from code section)
- \local_flow_context = 0xffffd38d'4b006f40\ — valid pool address
## Root Cause
Use-after-free race in \
et_ebpf_ext_sock_ops.c\:
1. **Flow creation** (line 523): \REFERENCE_FILTER_CONTEXT\ is called, and \local_flow_context->filter_context = filter_context\ is set.
2. **Filter teardown**: The filter context's reference count reaches zero and the memory is freed. The freed pool memory is reused by the kernel (now mapped to code pages).
3. **WFP flow deletion callback** (line 606-658): \
et_ebpf_extension_sock_ops_flow_delete\ fires asynchronously via \ cpip!LruCleanupDpcRoutine\.
4. Line 623: \ilter_context = local_flow_context->filter_context\ — reads the **stale pointer** to freed memory.
5. Line 624: \ilter_context == NULL\ — not NULL (stale pointer is non-zero), passes check.
6. Line 628: \ilter_context->base.context_deleting\ — reads from kernel text, happens to read a value that doesn't trigger the early exit.
7. Line 632: \KeAcquireSpinLock(&filter_context->lock, &irql)\ — attempts to write to read-only kernel text → **BugCheck 0xBE**.
## Race Window
The \context_deleting\ check at line 628 is a TOCTOU — the filter context can be freed between the check and the spinlock acquisition at line 632. Even if \context_deleting\ were checked atomically, it doesn't prevent the use-after-free because the filter context memory is already freed by the time the flow deletion callback fires.
The reference taken at flow creation (line 523) should keep the filter context alive until the flow is deleted (line 652: \DEREFERENCE_FILTER_CONTEXT\). If the filter context is being freed before the flow deletion callback runs, the reference counting has a bug — either the reference wasn't properly taken, or it was prematurely released elsewhere.
## Reproduction
Observed in CI driver tests on server2022 (Debug build). The crash occurs during \ cpip!LruCleanupDpcRoutine\ which runs as a DPC to clean up stale TCP/UDP flows. This suggests the race is triggered by network traffic during test teardown.
## Affected Code
- \
etebpfext/net_ebpf_ext_sock_ops.c\, function \
et_ebpf_extension_sock_ops_flow_delete\, line 632
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.