Menu

#240 7.10-pre3 segfault on invalid URLs

closed-fixed
libcurl (356)
7
2013-06-21
2002-09-30
No

I have an application which managed to pass a URL with
an invalid scheme
to libcurl (the URL format was basically 'error:foo').

With libcurl 7.10-pre3 and 7.10-pre4 (on linux Mandrake
8.2, gcc 2.96, glibc-2.2.4),
this resulted in a segfault in hostip.c, line 495.

Here's the backtrace:
-------------------------------------------------

#0 hostcache_fixoffset (h=0x80656a0, offset=0) at
hostip.c:495
#1 0x000000c8 in ?? ()
#2 0x4001c04d in Curl_getaddrinfo (data=0x8050660,
hostname=0x8064fcc "a:", port=80, bufp=0xbffff3ec) at
hostip.c:616
#3 0x4001bde5 in Curl_resolv (data=0x8050660,
hostname=0x8064fcc "a:", port=80) at hostip.c:256
#4 0x40027318 in CreateConnection (data=0x8050660,
in_connect=0xbffff6d8) at url.c:2636
#5 0x40027734 in Curl_connect (data=0x8050660,
in_connect=0xbffff6d8) at url.c:2782
#6 0x40030ef9 in Curl_perform (data=0x8050660) at
transfer.c:1238
#7 0x4003133e in curl_easy_perform (curl=0x8050660) at
easy.c:245

------------------------------------------------
I couldn't reproduce it with the curl command line, but
I managed to
duplicate it with the following trivial code, taken
from simple.c:

-------------------------------------------------

#include <stdio.h>
#include <curl/curl.h>

int main(int argc, char **argv)
{
CURL *curl;
CURLcode res;
printf("Version=%s\n",curl_version());

curl = curl_easy_init();
if(curl && argc) {
curl_easy_setopt(curl, CURLOPT_URL, argv[1]);
res = curl_easy_perform(curl);
printf("Result code = %d\n",res);

curl_easy_cleanup(curl);
}
return 0;
}
-------------------------------------------------

When run with regular arguments, 'http://foo' etc. all
is fine. When
run with 'a:' or 'error:' or whatever, it segfaults. It
seems ok under
7.9.8. Further testing seemed to narrow the issue to
addresses which
look like they could be parseable as IPv6 addresses
(':' delimited),
but turn out not to be - basically, anything starting
'0-9' or 'a-f'
faults, otherwise it's ok.

(foo:, bar: baz: fault, but not xyzzy: or plugh:)

Tracing back from the fault in hostcache_fixoffset, it
looks like the
behaviour is governed by the return value in hostip.c,
line 600:

-------------------------------------------------
hostip.c:600: (7.10-pre3):
/* Linux */
while((res=gethostbyname_r(hostname,
(struct hostent *)buf,
(char *)buf +
sizeof(struct hostent),
step_size -
sizeof(struct hostent),
&h, /* DIFFERENCE */
&h_errnop))==ERANGE) {
step_size+=200;
}
-------------------------------------------------

'hostname' here is set to the entire input string for
unrecognised
schemes ('foo:'), whereas recognised schemes make the
hostname just
the alpha-numeric part (no ':').

The crash seems to be triggered by differing behaviour in
gethostbyname_r when given valid hostnames compared to
(what seem to be)
raw addresses.

For unresolvable regular names (including 'illegal
names', like 'z:',
gethostbyname_r returns

res=2

which libcurl returns as an unresolveable host error
(actually
buf->h_name and buf->h_aliases also are set to point to
'something',
even though they are unused), whereas for hostnames
that look like
resolvable addresses but turn out not to be (a:, b:
127.0.0.1:),
gethostbyname_r returns

res=0

indicating success, but buf->h_name and buf->h_aliases
are left untouched
and therefore remain NULL, which causes the segfault
shortly afterwards
at h->h_aliases[0] in hostcache_fixoffset: (hostip.c:494):

-------------------------------------------------
h->h_aliases=(char **)((long)h->h_aliases+offset);
while(h->h_aliases[i]) {
h->h_aliases[i]=(char *)((long)h->h_aliases[i]+offset);
i++;
}
-------------------------------------------------

h_errnop doesn't seem to be modified in either case, so
I think this
must be a resolver bug, but in any case, here's an
attempted fix:

-------------------------------------------------
--- hostip.c.orig Mon Sep 30 16:01:34 2002
+++ hostip.c Mon Sep 30 16:04:53 2002
@@ -609,7 +609,7 @@
#ifdef MALLOCDEBUG
infof(data, "gethostbyname_r() uses %d bytes\n",
step_size);
#endif
- if(!res) {
+ if(!res && ((struct hostent *)buf)->h_name) {
int offset;
h=(struct hostent *)realloc(buf, step_size);
offset=(long)h-(long)buf;

-------------------------------------------------
(attached also).

This fix 'works for me' on 7.10-pre3 and 7.10-pre4. I'm
not sure why
this doesn't show up on 7.9.8, as I can't see any major
changes in this
area.

I don't know if this applies to any of the other
variants of gethostbyname used by other platforms.

Cris

Discussion

  • Cris Bailiff

    Cris Bailiff - 2002-09-30

    simple patch for ambiguous hostnames

     
  • Daniel Stenberg

    Daniel Stenberg - 2002-09-30

    Logged In: YES
    user_id=1110

    I can verify that this is indeed a glibc flaw.

    This problem does not appear with glibc 2.1.2 but happens
    with 2.2.4.

    I'll now try your work-around and see what happens in more
    detail.

     
  • Daniel Stenberg

    Daniel Stenberg - 2002-09-30
    • priority: 5 --> 7
     
  • Daniel Stenberg

    Daniel Stenberg - 2002-09-30

    Logged In: YES
    user_id=1110

    Wrong, 2.1.2 does this as well, my previous test was a
    "ipv6-enabled" one.

     
  • Daniel Stenberg

    Daniel Stenberg - 2002-09-30
    • status: open --> open-fixed
     
  • Daniel Stenberg

    Daniel Stenberg - 2002-09-30

    Logged In: YES
    user_id=1110

    Ok, while I think your fix indeed might be good, it requires
    the buffer to be cleared before we do the name resolve call.
    I did this little patch instead and retried with the bad
    cases and it worked, it also seems to work good on all the
    good cases.

    Could you have a look and see that it seems to work for you too?

    I wish Linux man pages would include gethostbyname_r with a
    proper and detailed description... :-(

    diff -u -r1.73 hostip.c
    --- hostip.c 3 Sep 2002 11:53:00 -0000 1.73
    +++ hostip.c 30 Sep 2002 07:57:16 -0000
    @@ -605,6 +605,8 @@
    &h_errnop))==ERANGE) {
    step_size+=200;
    }
    + if(!h) /* failure */
    + res=1;

    #ifdef MALLOCDEBUG
    infof(data, "gethostbyname_r() uses %d bytes\n",
    step_size);

     
  • Cris Bailiff

    Cris Bailiff - 2002-09-30

    Logged In: YES
    user_id=38466

    Yes, this version of the fix also 'works for me'.

    I looked at the other returns, but didn't notice 'h' was
    NULL in the error case. Better than grovelling around in
    stuct hostent, certainly.

    Cris

     
  • Daniel Stenberg

    Daniel Stenberg - 2002-10-01
    • status: open-fixed --> closed-fixed
     
  • Daniel Stenberg

    Daniel Stenberg - 2002-10-01

    Logged In: YES
    user_id=1110

    Fixed. Closing.

     
MongoDB Logo MongoDB