inet: Fix getnameinfo (NI_NOFQDN) race condition (BZ#28566)

The 'not_first' is accessed on nrl_domainname() in a non atomically
way, although it is only updated after the lock is taken.

This patch fix the double-checked locking by using acquire-release
atomic operation instead of plain load and by moving the 'not_first'
store only after 'domain' is actually set.

Checked on x86_64-linux-gnu.

Reviewed-by: DJ Delorie <dj@redhat.com>
This commit is contained in:
Adhemerval Zanella 2021-11-10 14:40:30 -03:00
parent edddffc9df
commit c54d689269

View File

@ -83,103 +83,111 @@ libc_freeres_ptr (static char *domain);
now ignored. */
#define DEPRECATED_NI_IDN 192
static char *
nrl_domainname (void)
static void
nrl_domainname_core (struct scratch_buffer *tmpbuf)
{
static int not_first;
char *c;
struct hostent *h, th;
int herror;
if (! not_first)
while (__gethostbyname_r ("localhost", &th,
tmpbuf->data, tmpbuf->length,
&h, &herror))
{
__libc_lock_define_initialized (static, lock);
__libc_lock_lock (lock);
if (! not_first)
if (herror == NETDB_INTERNAL && errno == ERANGE)
{
char *c;
struct hostent *h, th;
int herror;
struct scratch_buffer tmpbuf;
if (!scratch_buffer_grow (tmpbuf))
return;
}
else
break;
}
scratch_buffer_init (&tmpbuf);
not_first = 1;
if (h != NULL && (c = strchr (h->h_name, '.')) != NULL)
domain = __strdup (++c);
else
{
/* The name contains no domain information. Use the name
now to get more information. */
while (__gethostname (tmpbuf->data, tmpbuf->length))
if (!scratch_buffer_grow (tmpbuf))
return;
while (__gethostbyname_r ("localhost", &th,
tmpbuf.data, tmpbuf.length,
if ((c = strchr (tmpbuf->data, '.')) != NULL)
domain = __strdup (++c);
else
{
/* We need to preserve the hostname. */
const char *hstname = strdupa (tmpbuf->data);
while (__gethostbyname_r (hstname, &th,
tmpbuf->data,
tmpbuf->length,
&h, &herror))
{
if (herror == NETDB_INTERNAL && errno == ERANGE)
{
if (!scratch_buffer_grow (&tmpbuf))
goto done;
if (!scratch_buffer_grow (tmpbuf))
return;
}
else
break;
}
if (h && (c = strchr (h->h_name, '.')))
if (h != NULL && (c = strchr(h->h_name, '.')) != NULL)
domain = __strdup (++c);
else
{
/* The name contains no domain information. Use the name
now to get more information. */
while (__gethostname (tmpbuf.data, tmpbuf.length))
if (!scratch_buffer_grow (&tmpbuf))
goto done;
struct in_addr in_addr;
if ((c = strchr (tmpbuf.data, '.')))
domain = __strdup (++c);
else
in_addr.s_addr = htonl (INADDR_LOOPBACK);
while (__gethostbyaddr_r ((const char *) &in_addr,
sizeof (struct in_addr),
AF_INET, &th,
tmpbuf->data,
tmpbuf->length,
&h, &herror))
{
/* We need to preserve the hostname. */
const char *hstname = strdupa (tmpbuf.data);
while (__gethostbyname_r (hstname, &th,
tmpbuf.data, tmpbuf.length,
&h, &herror))
if (herror == NETDB_INTERNAL && errno == ERANGE)
{
if (herror == NETDB_INTERNAL && errno == ERANGE)
{
if (!scratch_buffer_grow (&tmpbuf))
goto done;
}
else
break;
if (!scratch_buffer_grow (tmpbuf))
return;
}
if (h && (c = strchr(h->h_name, '.')))
domain = __strdup (++c);
else
{
struct in_addr in_addr;
in_addr.s_addr = htonl (INADDR_LOOPBACK);
while (__gethostbyaddr_r ((const char *) &in_addr,
sizeof (struct in_addr),
AF_INET, &th,
tmpbuf.data, tmpbuf.length,
&h, &herror))
{
if (herror == NETDB_INTERNAL && errno == ERANGE)
{
if (!scratch_buffer_grow (&tmpbuf))
goto done;
}
else
break;
}
if (h && (c = strchr (h->h_name, '.')))
domain = __strdup (++c);
}
break;
}
}
done:
scratch_buffer_free (&tmpbuf);
}
__libc_lock_unlock (lock);
if (h != NULL && (c = strchr (h->h_name, '.')) != NULL)
domain = __strdup (++c);
}
}
}
}
static char *
nrl_domainname (void)
{
static int not_first;
if (__glibc_likely (atomic_load_acquire (&not_first) != 0))
return domain;
__libc_lock_define_initialized (static, lock);
__libc_lock_lock (lock);
if (atomic_load_relaxed (&not_first) == 0)
{
struct scratch_buffer tmpbuf;
scratch_buffer_init (&tmpbuf);
nrl_domainname_core (&tmpbuf);
scratch_buffer_free (&tmpbuf);
atomic_store_release (&not_first, 1);
}
__libc_lock_unlock (lock);
return domain;
};