Bug 20532 - getaddrinfo uses errno and h_errno without guaranteeing they're set, wrong errors returned by gaih_inet when lookup functions are not found.
Summary: getaddrinfo uses errno and h_errno without guaranteeing they're set, wrong er...
Status: RESOLVED FIXED
Alias: None
Product: glibc
Classification: Unclassified
Component: nss (show other bugs)
Version: unspecified
: P2 normal
Target Milestone: 2.27
Assignee: Florian Weimer
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2016-08-29 07:15 UTC by David Sansome
Modified: 2018-03-31 12:43 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed: 2017-08-11 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description David Sansome 2016-08-29 07:15:16 UTC
When no gethostbyname*_r lookup functions can be loaded from a module (most commonly when a module is listed in /etc/nsswitch.conf that does not exist), gaih_inet falls into this block added in revision 3d04f5db20c8f0d1ba3881b5f5373586a18cf188 for #15339:

  if (errno != 0 && errno != ENOENT)
    __set_h_errno (NETDB_INTERNAL);

but there is no guarantee that errno was set - it's not set by __nss_lookup_function, and other previous lookup functions don't have to set it.  This is problem #1.

Problem #2 is lower down:

  if (h_errno == NETDB_INTERNAL)
    {
      result = -EAI_SYSTEM;
      goto free_and_return;
    }

this time there's no guarantee that h_errno is set, so it probably still has the same value as the last call to gaih_inet.

To reproduce:

$ grep hosts /etc/nsswitch.conf
hosts:          files dns doesnotexist

$ cat test.c
#include <netdb.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>

void gai(const char* addr) {
  struct addrinfo* res;
  int ret = getaddrinfo(addr, "80", NULL, &res);
  if (ret != 0) {
    printf("\"%s\" failed: %d, errno=%d\n", addr, ret, errno);
  } else {
    printf("\"%s\" succeeded\n", addr);
    freeaddrinfo(res);
  }
}

int main(int argc, char** argv) {
  char hostname[1024];
  gethostname(hostname, sizeof(hostname) - 1);

  gai(hostname);           /* Returns 0. */
  errno = EAGAIN;
  gai("does.not.resolve"); /* Returns EAI_SYSTEM, should return EAI_NONAME. */
  gai(hostname);           /* Returns EAI_SYSTEM, should return 0. */

  return 0;
}


Output:
$ gcc -o test test.c && ./test
"[my hostname]" succeeded
"does.not.resolve" failed: -11, errno=11
"[my hostname]" failed: -11, errno=11


Expected:
$ gcc -o test test.c && ./test
"[my hostname]" succeeded
"does.not.resolve" failed: -2, errno=11
"[my hostname]" succeeded


I'm not sure how to fix this because I'm not sure what errors revision 3d04f5db20c8f0d1ba3881b5f5373586a18cf188 is trying to catch.
Errors loading nss modules don't seem to be reported through errno (and I think everything's cached after the first call anyway?).

Maybe we should clear errno at the start of gaih_inet, but #19445 says we should reset it again before we exit.
Comment 1 Florian Weimer 2016-08-29 09:41:19 UTC
Is this related to these mailing list threads?

  https://sourceware.org/ml/libc-alpha/2016-07/msg00341.html
  https://sourceware.org/ml/libc-alpha/2016-07/msg00618.html
Comment 2 Siddhesh Poyarekar 2016-08-29 10:33:51 UTC
(In reply to Florian Weimer from comment #1)
> Is this related to these mailing list threads?
> 
>   https://sourceware.org/ml/libc-alpha/2016-07/msg00341.html
>   https://sourceware.org/ml/libc-alpha/2016-07/msg00618.html

That is problem #2.  Problem #1 is different and may just be a matter of resetting errno before trying callbacks.
Comment 3 Florian Weimer 2017-08-02 14:55:28 UTC
Note that gethosts is also buggy in this regard:

  while (1) {								      \
    rc = 0;								      \
    status = DL_CALL_FCT (fct, (name, _family, &th,			      \
				tmpbuf->data, tmpbuf->length,		      \
				&rc, &herrno, NULL, &localcanon));	      \
    if (rc != ERANGE || herrno != NETDB_INTERNAL)			      \
      break;								      \
    if (!scratch_buffer_grow (tmpbuf))					      \
      {									      \
	result = -EAI_MEMORY;						      \
	goto free_and_return;						      \
      }									      \
  }									      \
  if (status == NSS_STATUS_SUCCESS && rc == 0)				      \
    h = &th;								      \
  else									      \
    h = NULL;								      \
  if (rc != 0)								      \

It assumes that rc (essentially errno) and herrno are valid for all status return values.  In reality, at least nss_files can return NSS_STATUS_SUCCESS with clobbered errno and h_errno values.
Comment 4 Florian Weimer 2017-08-07 15:02:38 UTC
I filed bug 21915 for the issue raised in comment 3.
Comment 5 Florian Weimer 2018-01-12 11:14:40 UTC
Problem #1 should be fixed by this commit:

commit ad816a5e00ce891a2cea8187638fa0e00f83aaf6
Author: Florian Weimer <fweimer@redhat.com>
Date:   Fri Sep 1 08:57:28 2017 +0200

    getaddrinfo: Properly set errno for NSS function lookup failure
Comment 6 Sourceware Commits 2018-01-12 11:16:49 UTC
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU C Library master sources".

The branch, master has been updated
       via  63b52889c35b367cf20896442203bbe5d123058c (commit)
      from  9a08a366a7e7ddffe62113a9ffe5e50605ea0924 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=63b52889c35b367cf20896442203bbe5d123058c

commit 63b52889c35b367cf20896442203bbe5d123058c
Author: Florian Weimer <fweimer@redhat.com>
Date:   Fri Jan 12 12:15:38 2018 +0100

    Add missing reference to bug 20532

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

Summary of changes:
 ChangeLog |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)
Comment 7 Sourceware Commits 2018-01-12 12:34:55 UTC
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU C Library master sources".

The branch, release/2.26/master has been updated
       via  7d2672a47b24c6991ddbcc7b65a5086caed4596a (commit)
      from  268bd5f053204b80e771169e55b45704c04d77ad (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=7d2672a47b24c6991ddbcc7b65a5086caed4596a

commit 7d2672a47b24c6991ddbcc7b65a5086caed4596a
Author: Florian Weimer <fweimer@redhat.com>
Date:   Fri Jan 12 13:32:51 2018 +0100

    Add missing reference to bug 20532

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

Summary of changes:
 ChangeLog |    1 +
 NEWS      |    1 +
 2 files changed, 2 insertions(+), 0 deletions(-)