This is the mail archive of the glibc-bugs@sources.redhat.com mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[Bug libc/515] gethostbyname_r() returns incorrect error for certain sizes (or alignment)


------- Additional Comments From jamie at shareable dot org  2005-05-09 13:22 -------
Not much code uses gethostbyname_r, and unfortunately the Glibc version has a
bit of a reputation for problems.  (Look in the code for libcurl for comments:
it no longer uses a dynamically allocated buffer, and just uses a large buffer
without resizing now because some older Glibc versions returned EAGAIN instead
of ERANGE when the buffer's too small, and other Glibc versions returned EAGAIN
when a lookup failed...)

Anyway, I just discovered the same bug as being reported here.  With a too-small
buffer, under some circumstances (I found it when resolving CNAMEs over a
certain length, critically dependent on buffer size):

     Glibc-2.3.5 returns:
        ret == EINVAL, errno == EINVAL, *h_errnop == 3 (NO_RECOVERY)

     Glibc-2.3.2-27.9.7 (RH9) returns:
        ret == 0, errno == ERANGE, *h_errnop== 3 (NO_RECOVERY)

Clearly an important bit of code is this from Glibc-2.3.5, nss/getXXbyYY_r.c:

     int res;
     if (status == NSS_STATUS_SUCCESS || status == NSS_STATUS_NOTFOUND)
       res = 0;
     /* Don't pass back ERANGE if this is not for a too-small buffer.  */
     else if (errno == ERANGE && status != NSS_STATUS_TRYAGAIN)
       res = EINVAL;
   #ifdef NEED_H_ERRNO
     /* These functions only set errno if h_errno is NETDB_INTERNAL.  */
     else if (status == NSS_STATUS_TRYAGAIN && *h_errnop != NETDB_INTERNAL)
       res = EAGAIN;
   #endif
     else
       return errno;

     __set_errno (res);
     return res;

The three values, status, errno and *h_errnop, are set in the suspicious code of
glibc/resolv/* and glibc/resolv/nss_dns/*.

Following are some ideas about the resolv/ code for the next person to look at
more closely.

1. In glibc/resolv/nss_dns/dns-network.c, getanswer_r():

          if (errno == EMSGSIZE)
            {
              errno = ERANGE;
              return NSS_STATUS_TRYAGAIN;
            }

   Is it missing an assignment to *h_errnop?

   All the _other_ places in glibc/resolv/nss_dns/*.c which set errno to ERANGE
and return NSS_STATUS_TRYAGAIN do one more thing: They set *h_errnop to
NETDB_INTERNAL.  The above code snipper is not consistent with them.

2. In glibc/resolv/*.c, *h_errnop or h_errno are set to NO_RECOVERY in quite a
lot of places.  Should some of them be non-fatal, setting *h_errnop to
NETDB_INTERNAL, errno to ERANGE, and returning NSS_STATUS_TRYAGAIN?

3. In glibc/resolv/gethnamaddr.c, h_errno is set in quite a lot of places using
__set_h_errno.  Is that appropriate for gethostbyname_r() calls?  The h_errno
value should be stored in *h_errno_p, _not_ in the h_errno variable, right?

Just a few ideas there.  And to add another person to the list who've been stung
by this bug.

One final question: Given the existence of the bug: is there a "safe" buffer
size to use with gethostbyname_r where we can be sure this bug doesn't occur? 
Glibc uses 1024 internally (in gethostbyname) - is that a safe value to use?

-- Jamie

-- 


http://sources.redhat.com/bugzilla/show_bug.cgi?id=515

------- You are receiving this mail because: -------
You are on the CC list for the bug, or are watching someone who is.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]