Navigation Menu

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bad HTTP response causing 'double free detected in tcache 2' #11101

Closed
twrecked opened this issue May 11, 2023 · 5 comments
Closed

Bad HTTP response causing 'double free detected in tcache 2' #11101

twrecked opened this issue May 11, 2023 · 5 comments

Comments

@twrecked
Copy link

I did this

I have an old device I need to support that returns badly formatted HTTP responses for certain requests. I noticed that if I reuse a handle I get a SIGABRT in curl_dbg_realloc.

I managed to reproduce it with the following code:

#include <curl/curl.h>

int
main(int argc, char** argv)
{
	CURL* cl = curl_easy_init();
	curl_easy_setopt(cl, CURLOPT_URL, "https://google.com");
	curl_easy_perform(cl);

	curl_easy_setopt(cl, CURLOPT_URL, "http://127.0.0.1:12000");
	curl_easy_perform(cl);
	return 0;
}

And using the following badly formed header:

HTTP/1.1 200 OK
	Access-Control-Allow-Origin: *
	Connection: Keep-Alive
	Content-Type: text/html; charset=utf-8
Date: Wed, 10 May 2023 14:58:08 GMT

<!DOCTYPE html>
<html lang=\en\></html>

Which I serve with cat index.html | nc -l -C 12000.

The error I'm seeing is

free(): double free detected in tcache 2
Aborted (core dumped)

And the back trace shows

#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:49
#1  0x00007ffff7735086 in __GI_abort () at abort.c:79
#2  0x00007ffff778cb80 in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0x7ffff789a845 "%s\n")
    at ../sysdeps/posix/libc_fatal.c:155
#3  0x00007ffff779454a in malloc_printerr (str=str@entry=0x7ffff789cc58 "free(): double free detected in tcache 2") at malloc.c:5389
#4  0x00007ffff779681a in _int_free (av=av@entry=0x7ffff7ad2bc0 <main_arena>, p=p@entry=0x636db0, have_lock=have_lock@entry=1) at malloc.c:4232
#5  0x00007ffff7798845 in _int_realloc (av=av@entry=0x7ffff7ad2bc0 <main_arena>, oldp=oldp@entry=0x636db0, oldsize=oldsize@entry=128, nb=160)
    at malloc.c:4655
#6  0x00007ffff77999f6 in __GI___libc_realloc (oldmem=0x636dc0, bytes=152) at malloc.c:3255
#7  0x00007ffff7b3403d in curl_dbg_realloc (ptr=0x636dc8, wantedsize=136, line=118, source=0x7ffff7b973c0 "strdup.c") at memdebug.c:265
#8  0x00007ffff7b60bcc in Curl_saferealloc (ptr=0x636dc8, size=136) at strdup.c:118
#9  0x00007ffff7b170eb in unfold_value (data=0x623f38, value=0x636888 "\tAccess-Control-Allow-Origin: *\n", vlen=31) at headers.c:252
#10 0x00007ffff7b1727e in Curl_headers_push (data=0x623f38, header=0x636888 "\tAccess-Control-Allow-Origin: *\n", type=1 '\001')
    at headers.c:302
#11 0x00007ffff7b4c934 in chop_write (data=0x623f38, type=2, optr=0x636888 "\tAccess-Control-Allow-Origin: *\n", olen=32) at sendf.c:332
#12 0x00007ffff7b4ca75 in Curl_client_write (data=0x623f38, type=2, ptr=0x636888 "\tAccess-Control-Allow-Origin: *\n", len=32) at sendf.c:386
#13 0x00007ffff7b234be in Curl_http_readwrite_headers (data=0x623f38, conn=0x637258, nread=0x7fffffffd898, stop_reading=0x7fffffffd896)
    at http.c:4328
#14 0x00007ffff7b682fb in readwrite_data (data=0x623f38, conn=0x637258, k=0x624008, didwhat=0x7fffffffd94c, done=0x7fffffffd9f3, 
    comeback=0x7fffffffd9c7) at transfer.c:522
#15 0x00007ffff7b696c0 in Curl_readwrite (conn=0x637258, data=0x623f38, done=0x7fffffffd9f3, comeback=0x7fffffffd9c7) at transfer.c:1119
#16 0x00007ffff7b40461 in multi_runsingle (multi=0x623c18, nowp=0x7fffffffdb20, data=0x623f38) at multi.c:2443
#17 0x00007ffff7b40d9f in curl_multi_perform (multi=0x623c18, running_handles=0x7fffffffdb74) at multi.c:2729
#18 0x00007ffff7b04554 in easy_transfer (multi=0x623c18) at easy.c:668
#19 0x00007ffff7b04785 in easy_perform (data=0x623f38, events=false) at easy.c:758
#20 0x00007ffff7b047cf in curl_easy_perform (data=0x623f38) at easy.c:777
#21 0x0000000000400778 in main (argc=1, argv=0x7fffffffdda8) at test.c:24

A couple of things:

  • is reusing the handle valid?
  • if you comment out the request to Google it works fine, you need the good response before the bad one

I expected the following

When I use a valid response I see the following:

<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>301 Moved</TITLE></HEAD><BODY>
<H1>301 Moved</H1>
The document has moved
<A HREF="https://www.google.com/">here</A>.
</BODY></HTML>
<!DOCTYPE html>
<html lang=\en\></html>

curl/libcurl version

curl 8.0.1 (x86_64-pc-linux-gnu) libcurl/8.0.1 OpenSSL/3.0.2 zlib/1.2.11 brotli/1.0.9 libidn2/2.3.2 libpsl/0.21.0 (+libidn2/2.3.2)
Release-Date: 2023-03-20
Protocols: dict file ftp ftps gopher gophers http https imap imaps mqtt pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: alt-svc AsynchDNS brotli Debug HSTS HTTPS-proxy IDN IPv6 Largefile libz NTLM NTLM_WB PSL SSL threadsafe TLS-SRP TrackMemory UnixSockets

Note, 7.81.0 curl shows no such issue.

operating system

Linux skippy 5.15.0-71-generic #78-Ubuntu SMP Tue Apr 18 09:00:29 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux

Thanks for reading, if I haven't provided enough information let me know.

@dfandrich
Copy link
Contributor

I'm able to reproduce this on git HEAD using your sample program. I would have expected this draft test case to show the problem as well, but curl_easy_perform returns CURLE_BAD_FUNCTION_ARGUMENT when this is run, probably because it uses the curl CLI and it's probably setting some option that changes the behaviour. I'm not going to have a chance to look at this more myself.

@dfandrich
Copy link
Contributor

For the record, reusing the handle is perfectly valid and this should not crash.

@dfandrich
Copy link
Contributor

N.B. I did find that CURLE_BAD_FUNCTION_ARGUMENT in the not-quite-working test case was coming from headers.c line 305. For some reason, in the draft test case data->state.prevhead is NULL whereas in your reproduction program it's the last header from google.com.

@bagder bagder self-assigned this May 11, 2023
bagder added a commit that referenced this issue May 11, 2023
The "prevhead" pointer is used for the headers storage but was not
cleared correctly in init, which made it possible to act up when a
handle is reused.

Reported-by: Steve Herrell
Fixes #11101
@bagder
Copy link
Member

bagder commented May 11, 2023

I made #11103 which at least for me stopped the bug from reproducing.

@twrecked
Copy link
Author

Thanks for the quick response.

#11103 fixed the issue for me.

bagder added a commit that referenced this issue May 12, 2023
@bagder bagder closed this as completed in cd1c611 May 12, 2023
bagder added a commit that referenced this issue May 12, 2023
Reproduces the isue #11101 and verifies the fix.

Verifies a17b2a503f
bch pushed a commit to bch/curl that referenced this issue Jul 19, 2023
The "prevhead" pointer is used for the headers storage but was not
cleared correctly in init, which made it possible to act up when a
handle is reused.

Reported-by: Steve Herrell
Fixes curl#11101
Closes curl#11103
bch pushed a commit to bch/curl that referenced this issue Jul 19, 2023
Reproduces the isue curl#11101 and verifies the fix.

Verifies a17b2a503f
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging a pull request may close this issue.

3 participants