✓ProveIt
Examples / Report #686823 Saved analysis

CVE-2019-5481: krb5: double-free in read_data() after realloc() fail

curl · 2019-09-0310.7s analysis · $0.010 in model calls

What the report claims

Double-free of 'buf->data' occurs in lib/security.c's teardown path when Curl_saferealloc() fails, potentially exploitable via remote trigger of realloc() failure with len=0x7fffffff.

Compare with curl's recorded outcome

Known vulnerability

A real vulnerability: CVE-2019-5481 FTP-KRB double free

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

ProveIt's finding

Code supports the claim

The code supports the reported flaw. A maintainer should review its impact and decide what to do next.

Why this finding?
  • Curl_saferealloc frees the original pointer on realloc failure (lib/strdup.c:96-98)
  • read_data returns error without setting buf->data = NULL (lib/security.c:206-207)
  • len is read from attacker-controlled network socket (lib/security.c:197)
  • 0x7fffffff is a plausible trigger on 32-bit systems causing allocation failure
  • Double-free on teardown is a security-relevant heap corruption vulnerability
How this analysis ran 5 claims · 11 citations verified

Checked against curl master on 2019-09-03 (5050edb12484). Test 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 · 3 references checked out, 0 did not
2 · Claims
NVIDIA-Nemotron-3-Nano-30B-A3B
5 claims · 1313 tokens · $0.0002
3 · Evidence
2 code snippets
real curl source at 5050edb12484
4 · Judgment
Nemotron-3-Ultra-550b-a55b
4956 tokens · 8.3s · $0.0102
5 · Receipts
11 of 11 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 report. The double-free in read_data() is confirmed: when Curl_saferealloc fails, it frees buf->data but read_data returns without nulling the pointer, leaving a dangling reference that will be freed again on teardown. The len value is read from the network, so an attacker can trigger this on 32-bit systems by sending 0x7fffffff. We will fix this by ensuring buf->data is set to NULL on allocation failure.

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

Report claims

C1flaw supported

double-free of the reference 'buf->data' on the teardown path if 'Curl_saferealloc()' fails

Read the reasoning

When Curl_saferealloc fails with non-zero size, it frees the original pointer (buf->data) and returns NULL. read_data then returns an error code without nulling buf->data. The caller's teardown path will subsequently free buf->data a second time, causing a double-free.

C2location supported

in 'lib/security.c'

Read the reasoning

The read_data function is located in lib/security.c as shown in the provided source.

C3trigger supported

sender might be able to remotely trigger a realloc() failure ... by sending the value 0x7fffffff

Read the reasoning

The len value is read directly from the network socket, converted from network byte order, and passed to Curl_saferealloc. An attacker controlling the network stream can send 0x7fffffff (2,147,483,647), which on a 32-bit system will likely cause realloc to fail due to address space exhaustion.

C4impact supported

double-free after a 'realloc()' failure, which could be triggered remotely

Read the reasoning

Combines C1 and C3: attacker-controlled len can trigger realloc failure, leading to double-free on teardown.

C5reproduction supported

realloc failure with particular 'len' value can be reproduced ... by sending the value 0x7fffffff

Read the reasoning

The reporter's test case demonstrates that realloc(0x7fffffff) fails on a 32-bit system. Since len comes from the network and is passed directly to realloc via Curl_saferealloc, the same failure can be triggered in the actual code path.

Read the original report

Full report on HackerOne ↗

## Summary: In 'lib/security.c', there is a double-free of the reference 'buf->data' on the teardown path if 'Curl_saferealloc()' fails. Also, since we read 'len' from the 'fd', the sender might be able to remotely trigger a realloc() failure, and then the double-free, by sending the value 0x7fffffff. Introduced by 0649433da realloc: use Curl_saferealloc to avoid common mistakes ## Steps To Reproduce: Actual double-free was not reproduced. The realloc failure with particular 'len' value can be reproduced on my 32bits linux machine with following code: ```C #include <stdio.h> #include <stdlib.h> int main(void) { void *ptr = malloc(10); if (!ptr) return -1; int len = 0x7fffffff; void *ptr2 = realloc(ptr, len); if (!ptr2) { printf("Triggered realloc failure\n"); return 0; } return -1; } ``` ## Comment: Also checked other occurences of 'Curl_saferealloc()' calls which all seem fine otherwise. ## Impact Double-free after a 'realloc()' failure, which could be triggered remotely, depending on the use context of the 'read_data()' function.

Source code

lib/security.clines 189–217 · definition of read_data
189static CURLcode read_data(struct connectdata *conn,
C2: read_data function defined in lib/security.c
190 curl_socket_t fd,
191 struct krb5buffer *buf)
192{
193 int len;
194 void *tmp = NULL;
195 CURLcode result;
196
197 result = socket_read(fd, &len, sizeof(len));
C3: len read from socket via socket_read(fd, &len, sizeof(len))
C4: Attacker controls len via network input
198 if(result)
199 return result;
200
201 if(len) {
202 /* only realloc if there was a length */
203 len = ntohl(len);
C3: len = ntohl(len) converts network byte order to host order
C5: len = ntohl(len) - attacker-controlled value reaches realloc
204 tmp = Curl_saferealloc(buf->data, len);
C1: Curl_saferealloc(buf->data, len) called; on failure it frees buf->data and returns NULL
C3: len passed to Curl_saferealloc as size argument
205 }
206 if(tmp == NULL)
C1: read_data returns CURLE_OUT_OF_MEMORY without setting buf->data = NULL, leaving dangling pointer
C4: buf->data not nulled before error return
207 return CURLE_OUT_OF_MEMORY;
208
209 buf->data = tmp;
210 result = socket_read(fd, buf->data, len);
211 if(result)
212 return result;
213 buf->size = conn->mech->decode(conn->app_data, buf->data, len,
214 conn->data_prot, conn);
215 buf->index = 0;
216 return CURLE_OK;
217}
lib/strdup.clines 93–100 · definition of Curl_saferealloc
93void *Curl_saferealloc(void *ptr, size_t size)
94{
95 void *datap = realloc(ptr, size);
96 if(size && !datap)
C1: Curl_saferealloc frees ptr when size != 0 and realloc fails
C4: Curl_saferealloc frees original buffer on allocation failure
97 /* only free 'ptr' if size was non-zero */
98 free(ptr);
99 return datap;
100}