HexBrain

curl · Report #3718265

Shared HSTS cache accessed without lock

N/Astatus
13upvotes
5 Jun 2026disclosed

How it was found

This is finding F5 in Andrew's report

https://github.com/curl/curl/blob/455bebc2c7/lib/hsts.c#L160-L168 https://github.com/curl/curl/blob/455bebc2c7/lib/http.c#L3571 https://github.com/curl/curl/blob/455bebc2c7/lib/url.c#L1441 https://github.com/curl/curl/blob/455bebc2c7/lib/url.c#L265 https://github.com/curl/curl/blob/455bebc2c7/lib/transfer.c#L613 https://github.com/curl/curl/blob/455bebc2c7/lib/curl_share.c#L459 https://github.com/curl/curl/blob/455bebc2c7/lib/hsts.c#L605-L613

When CURL_LOCK_DATA_HSTS is shared, data->hsts aliases share->hsts (curl_share.c:459). The CVE-2023-27537 fix added locking only to Curl_hsts_loadfiles (hsts.c:605-613). All other paths access the shared list without locking:

Curl_hsts_parse (http.c:3571) on incoming Strict-Transport-Security header → appends/removes entries
Curl_hsts_applies → hsts_check (url.c:1441) on every http:// URL → walks the list AND frees expired entries (hsts.c:166-167: Curl_node_remove(&sts->node); hsts_free(sts);)
Curl_hsts_save (url.c:265) in Curl_close → walks the list
Curl_hsts_loadcb (transfer.c:613) → appends entries

Race A (double-free): two threads each call hsts_check on the same expired entry; both reach line 166-167 with the same sts pointer; double-free + llist corruption.

Race B (UAF): thread 1 in Curl_hsts_parse appending (writes list->tail->next); thread 2 in hsts_check frees that tail node as expired.

Note hsts_check is not read-only despite the name — it mutates during lookup, so even "read" paths need exclusive locking.

Same bug class as CVE-2023-27537 (HSTS double-free), in code paths that fix did not cover. Contrast with cookies in the same function: http.c:3550-3553 wraps Curl_cookie_add in Curl_share_lock(CURL_LOCK_DATA_COOKIE); the HSTS call 18 lines later has no equivalent.

Suggested fix: wrap all four call sites in Curl_share_lock/unlock(data, CURL_LOCK_DATA_HSTS, CURL_LOCK_ACCESS_SINGLE). Note CURL_LOCK_ACCESS_SINGLE (exclusive), not shared, on every path including the lookup at url.c:1441 — hsts_check mutates the list during what looks like a read, so a reader/writer split would still race.

Verified by grep for CURL_LOCK_DATA_HSTS (only 4 hits: 2 in share.c registration, 2 in the loadfiles lock/unlock pair) and reading each unlocked call site.

Impact

double-free or UAF?

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original