"A heap-based buffer overflow was found in __nss_hostname_digits_dots(), which is used by the gethostbyname() and gethostbyname2() glibc function call. A remote attacker could use this flaw to execute arbitary code with the permissions of the user running the application."
Note that this is a HEAD link, so if there are changes after I post this they should appear. I don't claim to have spotted the suspicious code (it's not ... super-accessible), just wanted to provide a link to the file in question.
I'm not fully though my morning bootup process and so not really ready to grok this but, can anyone give a quick summary of why gethostbyname() needs to hit the heap at all, let alone with a realloc call? There's a maximum hostname length, and it's not huge. Also: isn't this function just saying "yes" or "no" to a candidate hostname? Can't it just say "no" if the hostname is super long?
> Avoid arbitrary limits on the length or number of any data structure, including file names, lines, files, and symbols, by allocating all data structures dynamically.
the entire hostname (including the delimiting dots but not a trailing dot) has a maximum of 253 ASCII characters
In general that is a good guideline, but when the standard (RFC1035) says there is an absolute limit, there is little value in going above that as it is likely that other systems won't be able to handle it. The added complexity of dynamic allocation is also an opportunity for bugs, like this one.
Again, this is especially silly if (as it appears to first glance) the bug is in a hostname validation function, and so flexible allocation could only ever be useful in the case of a hostname that must fail validation anyways.
In this case the bug isn't caused by dynamic allocation though, is it? The problem is the validation logic for detecting if the caller didn't allocate a big enough buffer when making the call.
In fact, it looks like if you get to the dynamic allocation section, it will fix the problem. One could argue that the whole problem stems from having a bug in a complex computation of buffer size to handle lots of different bits of data, rather than dynamically allocating the individual bits as needed.
gethostbyname() and friends fill in struct hostent:
struct hostent {
char *h_name; /* official name of host */
char **h_aliases; /* alias list */
int h_addrtype; /* host address type */
int h_length; /* length of address */
char **h_addr_list; /* list of addresses */
}
The pointers in the structure point into the buffer. There could be any number of host aliases or IP addresses.
That's true and a good point, but not (it seems) applicable to this particular function, which validates whether or not the name is one of two fixed-sized formats, right?
It looks like what's going on is that gethostbyname() calls __nss_hostname_digits_dots() which checks to see if the string you passed it was an IPv4 or IPv6 address rather than a name, and in that case it functions like inet_aton/inet_pton and converts the IP address string to a binary IP address as though the "name" 1.2.3.4 resolved to IP address 1.2.3.4.
In that specific case there are no aliases and exactly one IP address, but the buffer could still be too small (e.g. if caller-supplied with gethostbyname_r()).
Right, but they only need to do that computation because they're dynamically allocating storage. But the maximum size of a hostname is so small that hitting the allocator is costing them more than static allocation would.
"A heap-based buffer overflow was found in __nss_hostname_digits_dots(), which is used by the gethostbyname() and gethostbyname2() glibc function call. A remote attacker could use this flaw to execute arbitary code with the permissions of the user running the application."