curl · Report #3718265
Shared HSTS cache accessed without lock
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?