cloudflare / cloudflare/wirefilter

Five FFI value-ingestion setters have missing/inconsistent null-pointer validation — two produce a real SIGSEGV, not a panic

Open
#194 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
1.2k
Forks
122
Avg merge
5h 20m
Merged PRs (30d)
4

Description

Five `extern "C"` functions in `ffi/src/lib.rs` ingest a per-request field value into an
`ExecutionContext` from a raw pointer (or, for the two IP setters, a Rust reference) supplied by the
embedding host. Their null-pointer validation is inconsistent, and for two of the five, absent:

| Function | Null check? | Consequence of `NULL` |
|---|---|---|
| `wirefilter_add_json_value_to_execution_context` | **none** | `std::slice::from_raw_parts(json_ptr, json_len)` (line 549) reads from address 0 |
| `wirefilter_add_ipv4_value_to_execution_context` | **none** — takes `value: &[u8; 4]` | dereferenced unconditionally via `IpAddr::from(*value)` (line 620) |
| `wirefilter_add_ipv6_value_to_execution_context` | **none** — takes `value: &[u8; 16]`, same shape | not independently reproduced, expected identical by code-shape |
| `wirefilter_deserialize_json_to_execution_context` | `assert!(!json_ptr.is_null())` (line 523), unconditional regardless of `json_len` | any `(NULL, 0)` call panics; no `catch_panic` wrapper |
| `wirefilter_add_bytes_value_to_execution_context` | `assert!(!value_ptr.is_null())` (line 593), same shape | same as above |

Within four of these five (all but `deserialize_json`, which takes no separate name argument), the
`name_ptr` argument *is* validated via the `to_str!` macro's `assert!(!$ptr.is_null())` — so the
inconsistency is present inside a single function, not just across the file.

**Impact — two of the five are a real crash, not a controlled panic.** Confirmed via a compiled C
caller linked against the built `cdylib` (not a Rust-constructed value):

```
calling wirefilter_add_ipv4_value_to_execution_context(ctx, "ip.src", NULL) ...
(process exits with signal 11, SIGSEGV: invalid memory reference)
```

Confirmed in both debug and release. A Rust-level PoC (constructing the null value in Rust rather
than via a real C caller) shows the same result for the JSON setter:

```
$ cargo run --release --example poc_f018_add_json_value_null_oob
(json_ptr=NULL, json_len=16)
exit: 139 (= 128 + SIGSEGV)
```

The other two setters (`deserialize_json`, `add_bytes_value`) produce a controlled `SIGABRT` on
`(NULL, 0)` in both profiles — a process-killing DoS, not memory-unsafety.

Separately: `wirefilter_enable_panic_catcher()` is never called automatically anywhere in this crate
(`PANIC_CATCHER_ENABLED` defaults to `false`), so even those two `assert!`-guarded setters' panics
reach an uncaught process abort in any embedding that never explicitly enables it.

**Reachability:** requires the embedding host to pass a null pointer/reference through — plausibly by
representing an empty or absent field (no cookie, an empty header) as `(NULL, 0)`, a common enough C
convention that it's an easy embedding mistake. Not demonstrated as network-reachable end-to-end;
whether a specific embedding's request-handling path can be driven into passing a null value depends
on that embedding layer.

**Suggested fix:** add the same `assert!(!ptr.is_null())` (or a `Result`-returning check) that
`to_str!` and the two partially-guarded setters already use, to the JSON setter and both IP setters —
and treat `length == 0` as "empty" (skip the pointer check) for the `(ptr, len)`-style setters rather
than an error. Wrap all five in the existing `catch_panic` helper, matching
`wirefilter_parse_filter`/`wirefilter_match`.

Found with the [rust-in-peace](https://github.com/scadastrangelove/rust-in-peace) pipeline.

Contributor guide

Open the contributing guide

Research direction

Read ffi/src/lib.rs and compare the five value-ingestion setters with the to_str! checks and the catch_panic usage in wirefilter_parse_filter and wirefilter_match. Exercise the setters with NULL pointers from a compiled C caller, including zero-length inputs. Done means all five handle invalid pointers consistently, empty pointer-and-length inputs are treated as empty, and no uncaught panic or SIGSEGV occurs.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
api, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
65/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.