# Patch Analysis: CVE-2026-10536 (libcurl HTTP/2 stream-dependency UAF)

## Fix Commit

- Primary fix: `bfbff7852f050232edd3e5ca5c6bf2021c340f5a` — `http2: remove stream dependency tracking`
- Release tag containing the fix: `curl-8_21_0` (`3f00a2f6fa97f7721b65606954aac979dcb6caac`)
- Vulnerable reference tested: `6e3f8dc1f173b47de9a68516ce4b95bf25598c2f` (`curl-8_20_0`)

## What the Fix Changes

The patch removes the in-memory HTTP/2 stream-dependency tree between easy
handles. Concretely:

1. **`lib/setopt.c`** — The `CURLOPT_STREAM_DEPENDS` and
   `CURLOPT_STREAM_DEPENDS_E` cases in `setopt_pointers()` are changed from
   active tree-builders to no-ops:

   ```c
   case CURLOPT_STREAM_DEPENDS:
   case CURLOPT_STREAM_DEPENDS_E:
     /* not doing stream dependencies any longer, but accept options
      * for backward compatibility */
     break;
   ```

   The options are kept as deprecated no-ops so existing applications still
   compile and run, but they no longer create inter-handle links.

2. **`lib/url.c`** — The following functions are removed entirely:
   - `priority_remove_child()`
   - `Curl_data_priority_add_child()`
   - `data_priority_cleanup()`

   The only remaining priority-related code is a zeroing of the `set.priority`
   struct during handle setup and `Curl_data_priority_clear_state()` for
   per-transfer state, neither of which allocates or dereferences inter-handle
   pointers.

3. **`lib/url.h`** — The prototype for `Curl_data_priority_add_child()` is
   removed; the stub macro is kept for builds without HTTP/2.

## What Assumptions the Fix Makes

- The only way to create a dependency relationship between easy handles is via
  the public `CURLOPT_STREAM_DEPENDS` / `CURLOPT_STREAM_DEPENDS_E` options.
- No other internal API or legacy call path creates, copies, or retains the
  `set.priority.children` / `set.priority.parent` links.
- By making the option a no-op and deleting the tree-maintenance code, all
  possible UAF paths through the dependency tree are eliminated.

## What the Fix Does NOT Cover (and Why It Does Not Need To)

- **Other unrelated bugs:** The fix is specific to the stream-dependency tree.
  It does not address, for example, HTTP/2 stream priority state inside a
  single transfer (`data->state.priority`), which is unrelated to the inter-handle
  UAF.
- **Network-triggered paths:** The advisory describes an application-level API
  misuse; no remote input reaches the dependency tree.
- **The `curl_easy_duphandle()` shallow-copy issue:** In the vulnerable version,
  `dupset()` copies the entire `set` struct, so the cloned handle shares the
  same `children` pointer. This is not a separate bug class; it is another
  manifestation of the same dependency-tree lifetime bug and is also closed by
  removing the tree entirely.

## Comparison of Behavior Before and After the Fix

| Scenario | Vulnerable (`curl-8_20_0`) | Fixed (`curl-8_21_0`) |
|---|---|---|
| `curl_easy_setopt(CURLOPT_STREAM_DEPENDS)` then `curl_easy_reset(child)` + cleanup order | ASan heap-use-after-free in `data_priority_cleanup` / `priority_remove_child` | Option ignored, no allocation, no UAF |
| `curl_easy_duphandle(parent)` after setting dependency, then cleanup order | ASan heap-use-after-free on shared `Curl_data_prio_node` | No dependency tree to copy, no UAF |
| `curl_easy_reset(parent)` after child depends on it, then cleanup child | ASan heap-use-after-free on freed parent struct | No dependency tree, no UAF |

## Completeness Assessment

The fix is **complete** for the reported vulnerability class. Because the
feature that created the problematic cross-handle pointers is removed, there
is no remaining code that can free or dereference a dependency node from another
handle's lifetime. The variant analysis below confirms that alternate API paths
(`curl_easy_duphandle` and a reversed reset/cleanup order) still reach the same
UAF on the vulnerable commit but are clean on the fixed and latest releases.

## Threat-Model Note

The curl project does not document the deprecated stream-dependency options as
a security boundary; they are ordinary library API options. The vulnerability is
a memory-safety bug in those options, not a privilege boundary. The fix aligns
with the upstream deprecation by removing the feature.
