✓ProveIt
Examples / Report #948876 Saved analysis

CVE-2020-8231: Connect-only connections can use the wrong connection

curl · 2020-07-3110.1s analysis · $0.011 in model calls

What the report claims

A connect-only easy handle may reuse a connection address after the original connection times out, causing data intended for the original server to be sent to a different server.

Compare with curl's recorded outcome

Known vulnerability

A real vulnerability: CVE-2020-8231 wrong connect-only connection

Shown for comparison after analysis. The pipeline did not see this label.

ProveIt's finding

Needs human review

The available evidence does not settle the report. A maintainer should investigate.

Why this finding?
  • The report describes a plausible use-after-free / pointer-reuse flaw in connect-only connection handling with a concrete reproducer.
  • The provided curl source snippets (curl_multi_socket_action, curl_easy_send) are only entry points; the deciding connection management code (easy_connection, connection pool, connect-only association, CURLOPT_MAXAGE_CONN expiration) is not included.
  • All five claims are not_verifiable against the provided code. A human must examine the connection management logic in lib/url.c, lib/conncache.c, lib/easy.c (easy_connection), and multi.c (multi_socket) at the target revision.
What still needs checking
  • The easy_connection function implementation
  • Connection cache/pool management code (likely in lib/conncache.c)
  • How connect-only handles store and retrieve their connection pointer
  • CURLOPT_MAXAGE_CONN expiration handling and connection cleanup
How this analysis ran 5 claims · 0 citations verified

Checked against curl master on 2020-07-31 (633c9478c91f). Dev evaluation split. Citation verification checks that a referenced line was in the evidence; it does not prove the model interpreted it correctly.

1 · Grounding
grounded
no model · 1 references checked out, 0 did not
2 · Claims
NVIDIA-Nemotron-3-Nano-30B-A3B
5 claims · 2937 tokens · $0.0003
3 · Evidence
2 code snippets
real curl source at 633c9478c91f · 2 docs via Tavily
4 · Judgment
Nemotron-3-Ultra-550b-a55b
7105 tokens · 7.4s · $0.0107
5 · Receipts
0 of 0 citations verified
every citation must point at code the judge was shown
Review a draft reply to the reporter

Edit and verify before sending.

Thank you for the detailed report and reproducer. The issue you describe (connect-only handle reusing a freed connection's address after CURLOPT_MAXAGE_CONN timeout) is plausible and the custom allocator is a clever way to force deterministic reuse. However, the source snippets we have (curl_multi_socket_action, curl_easy_send) don't include the connection management logic that would confirm or deny the flaw. Could you point to the specific functions/files where connect-only handles track their connection (e.g., easy_connection, connection cache, CURLOPT_MAXAGE_CONN handling)? That would let us verify the fix.

Inspect all claims and source code 5 claims · 2 code excerpts

Report claims

C1flaw not verifiable

A connect-only easy handle can reuse a connection address after the original connection times out, leading to data being sent to the wrong server.

Read the reasoning

The claim describes a connection reuse flaw in connect-only handles after timeout. The provided source snippets only show curl_multi_socket_action and curl_easy_send entry points. The critical connection management logic (easy_connection function, connection pool, connect-only handle association, CURLOPT_MAXAGE_CONN handling) is not in the provided code. Cannot verify or contradict from given source.

C2location not verifiable

The issue occurs in the curl multi socket action handling of connect-only handles.

Read the reasoning

The claim locates the issue in curl_multi_socket_action handling of connect-only handles. The provided curl_multi_socket_action (lib/multi.c:2991-3001) only shows a thin wrapper calling multi_socket. The actual connection timeout and reuse logic would be in multi_socket or connection management code, which is not provided.

C3trigger not verifiable

When a connection times out and is closed, a new connection may be allocated at the same address and reused by a connect-only handle.

Read the reasoning

The claim describes the trigger mechanism: connection timeout, close, reallocation at same address, reuse by connect-only handle. The reporter's custom allocator (malloc_/free_) demonstrates deterministic address reuse, but curl's actual connection allocation/free logic and how connect-only handles track connections is not in the provided source.

C4impact not verifiable

Sensitive data intended for one server may be transmitted to a different server.

Read the reasoning

The impact claim follows from C1-C3. curl_easy_send (lib/easy.c:1145-1173) calls easy_connection to get the connection, but easy_connection is not provided. Without seeing how the connection pointer is stored and validated, cannot confirm data would go to the wrong server.

C5reproduction not verifiable

The provided sample program demonstrates deterministic address reuse causing data to be sent to port 1236 instead of port 1234.

Read the reasoning

The reproduction program is provided in the report and uses a custom caching allocator to force address reuse. Whether this actually triggers the described flaw in curl depends on curl's internal connection management, which is not in the provided source snippets.

Read the original report (excerpt)

Full report on HackerOne ↗

## Summary: If a connect-only easy handle is not read from or written to, its connection can time out and be closed. If a new connection is created it can be allocated at the same address, causing the easy handle to use the new connection. This new connection may not be connected to the same server as the old connection, which can allow sensitive information intended to go to the first server to instead go to the second server. This sequence of events would be uncommon in ordinary usage, so I have attached a sample program that implements a simple caching allocator, which causes the address to be re-used deterministically. According to git bisect, this behavior was introduced in commit 755083d. ## Steps To Reproduce: 1. Compile the source code below 1. Listen on ports 1234, 1235, and 1236 1. Run the compiled program 1. Notice that the data which was supposed to be sent to port 1234 is actually sent to port 1236 ## Supporting Material/References: ```c++ #include <iostream> #include <stdexcept> #include <thread> #include <chrono> #include <unordered_map> #include <string.h> #include <curl/curl.h> using namespace std::literals; static void require(bool b) { if (!b) throw std::runtime_error("Assertion failed"); } struct alloc { alloc *next_alloc; std::size_t size; }; std::unordered_map<std::size_t, alloc *> cached_allocations; void *malloc_(size_t size) { auto &ptr = cached_allocations[size]; if (ptr) { void *ret = (char *)ptr + sizeof(alloc); ptr = ptr->next_alloc; return ret; } auto new_ptr = (alloc *)calloc(1, size + sizeof(alloc)); new_ptr->next_alloc = nullptr; new_ptr->size = size; void *ret = ((char *)new_ptr) + sizeof(alloc); return ret; } void free_(void *ptr) { auto alloc_ptr = (alloc *)((char *)ptr - sizeof(alloc)); auto &last_alloc = cached_allocations[alloc_ptr->size]; alloc_ptr->next_alloc = last_alloc; last_alloc = alloc_ptr; } void *realloc_(void *ptr, size_t size) { auto alloc_ptr = (alloc *)((char *)ptr - sizeof(alloc)); auto new_alloc_ptr = (alloc *)realloc(alloc_ptr, size + sizeof(alloc)); new_alloc_ptr->size = size; return (char *)new_alloc_ptr + sizeof(alloc); } char *strdup_(const char *str) { auto size = strlen(str) + 1; auto new_str = (char *)malloc(size); return strcpy(new_str, str); } void *calloc_(size_t nmemb, size_t size) { auto full_size = nmemb*size; return malloc_(full_si …

Source code

lib/multi.clines 2991–3001 · definition of curl_multi_socket_action
2991CURLMcode curl_multi_socket_action(struct Curl_multi *multi, curl_socket_t s,
2992 int ev_bitmask, int *running_handles)
2993{
2994 CURLMcode result;
2995 if(multi->in_callback)
2996 return CURLM_RECURSIVE_API_CALL;
2997 result = multi_socket(multi, FALSE, s, ev_bitmask, running_handles);
2998 if(CURLM_OK >= result)
2999 Curl_update_timer(multi);
3000 return result;
3001}
lib/easy.clines 1145–1173 · definition of curl_easy_send
1145CURLcode curl_easy_send(struct Curl_easy *data, const void *buffer,
1146 size_t buflen, size_t *n)
1147{
1148 curl_socket_t sfd;
1149 CURLcode result;
1150 ssize_t n1;
1151 struct connectdata *c = NULL;
1152
1153 if(Curl_is_in_callback(data))
1154 return CURLE_RECURSIVE_API_CALL;
1155
1156 result = easy_connection(data, &sfd, &c);
1157 if(result)
1158 return result;
1159
1160 *n = 0;
1161 result = Curl_write(c, sfd, buffer, buflen, &n1);
1162
1163 if(n1 == -1)
1164 return CURLE_SEND_ERROR;
1165
1166 /* detect EAGAIN */
1167 if(!result && !n1)
1168 return CURLE_AGAIN;
1169
1170 *n = (size_t)n1;
1171
1172 return result;
1173}