Well, given the total lack of feeback on my two diffs,
and given that time is running out fast, let's jump the gun:

Theo de Raadt wrote on Thu, Feb 20, 2014 at 05:43:18PM -0700:
> kettenis@ wrote:

>> Indeed.  POSIX explicitly says:
>> 
>>   "No function in this volume of POSIX.1-2008 shall set errno to zero."
>> 
>> The standard is slightly ambiguous on what getpwnam_r() should do, but
>> the way I read it, it should not touch errno at all.  So it should
>> save errno at the start of the function, and restore it just before
>> return.

> That is the only sane approach.
> 
> I would like to see a diff in which at least solves the non-YP cases.
> The YP cases have other issues in other functions as well, that are
> somewhat known..

Here is a third diff, to be applied on top of my previous two,
doing what kettenis@ said:  let getpwnam_r() not touch errno at all.
Obviously, that is not a conservative approach and may have
unexpected consequences in some software, even if it's correct.

So, i'm now accepting OKs for either of the following:

 * my first diff,
   fixing just the return values of get*_r()
   without changing errno handling;
   i consider that quite safe.

 * my second diff,
   just avoiding clobbering of errno by close() and syslog()
   and setting errno = ERANGE when needed for the non-YP case;
   i consider that quite safe,
   it can be committed with or without the first one.

 * the cumulative diff including all three,
   given at the very bottom.
   That is NOT CONSERVATIVE, it changes two things in addition
   to the above:

    - getpw{nam,uid}_r() no longer sets errno even on serious errors.
    - If a match is found after any kind of YP error,
      getpw{nam,uid}() no longer set errno.

   Both make sense to me, but are significant changes.

 ----- 8< ----- schnipp ----- >8 ----- 8< ----- schnapp ----- >8 -----

### THIS IS MY THIRD DIFF:
--- getpwent.c.errno    Wed Feb 19 20:13:34 2014
+++ getpwent.c  Sat Feb 22 10:45:39 2014
@@ -741,8 +741,7 @@ fail:
                *pwretp = pwret;
        if (pwret == NULL)
                my_errno = errno;
-       if (!errno)
-               errno = saved_errno;
+       errno = saved_errno;
        _THREAD_PRIVATE_MUTEX_UNLOCK(pw);
        return (my_errno);
 }
@@ -751,9 +750,14 @@ struct passwd *
 getpwnam(const char *name)
 {
        struct passwd *pw = NULL;
+       int my_errno;
 
-       if (getpwnam_r(name, &_pw_passwd, _pw_string, sizeof _pw_string, &pw))
+       my_errno = getpwnam_r(name, &_pw_passwd, _pw_string,
+           sizeof _pw_string, &pw);
+       if (my_errno) {
                pw = NULL;
+               errno = my_errno;
+       }
        return (pw);
 }
 
@@ -796,8 +800,7 @@ fail:
                *pwretp = pwret;
        if (pwret == NULL)
                my_errno = errno;
-       if (!errno)
-               errno = saved_errno;
+       errno = saved_errno;
        _THREAD_PRIVATE_MUTEX_UNLOCK(pw);
        return (my_errno);
 }
@@ -806,9 +809,14 @@ struct passwd *
 getpwuid(uid_t uid)
 {
        struct passwd *pw = NULL;
+       int my_errno;
 
-       if (getpwuid_r(uid, &_pw_passwd, _pw_string, sizeof _pw_string, &pw))
+       my_errno = getpwuid_r(uid, &_pw_passwd, _pw_string,
+           sizeof _pw_string, &pw);
+       if (my_errno) {
                pw = NULL;
+               errno = my_errno;
+       }
        return (pw);
 }
 

 ----- 8< ----- schnipp ----- >8 ----- 8< ----- schnapp ----- >8 -----

### THIS IS THE CUMULATIVE DIFF 1+2+3:
Index: getpwent.c
===================================================================
RCS file: /cvs/src/lib/libc/gen/getpwent.c,v
retrieving revision 1.48
diff -u -p -r1.48 getpwent.c
--- getpwent.c  15 Nov 2013 22:32:55 -0000      1.48
+++ getpwent.c  22 Feb 2014 09:51:15 -0000
@@ -708,8 +708,12 @@ getpwnam_r(const char *name, struct pass
 {
        struct passwd *pwret = NULL;
        int flags = 0, *flagsp;
+       int my_errno = 0;
+       int saved_errno, tmp_errno;
 
        _THREAD_PRIVATE_MUTEX_LOCK(pw);
+       saved_errno = errno;
+       errno = 0;
        if (!_pw_db && !__initdb())
                goto fail;
 
@@ -727,23 +731,33 @@ getpwnam_r(const char *name, struct pass
                pwret = _pwhashbyname(name, buf, buflen, pw, flagsp);
 
        if (!_pw_stayopen) {
+               tmp_errno = errno;
                (void)(_pw_db->close)(_pw_db);
                _pw_db = NULL;
+               errno = tmp_errno;
        }
 fail:
        if (pwretp)
                *pwretp = pwret;
+       if (pwret == NULL)
+               my_errno = errno;
+       errno = saved_errno;
        _THREAD_PRIVATE_MUTEX_UNLOCK(pw);
-       return (pwret ? 0 : 1);
+       return (my_errno);
 }
 
 struct passwd *
 getpwnam(const char *name)
 {
        struct passwd *pw = NULL;
+       int my_errno;
 
-       if (getpwnam_r(name, &_pw_passwd, _pw_string, sizeof _pw_string, &pw))
+       my_errno = getpwnam_r(name, &_pw_passwd, _pw_string,
+           sizeof _pw_string, &pw);
+       if (my_errno) {
                pw = NULL;
+               errno = my_errno;
+       }
        return (pw);
 }
 
@@ -753,8 +767,12 @@ getpwuid_r(uid_t uid, struct passwd *pw,
 {
        struct passwd *pwret = NULL;
        int flags = 0, *flagsp;
+       int my_errno = 0;
+       int saved_errno, tmp_errno;
 
        _THREAD_PRIVATE_MUTEX_LOCK(pw);
+       saved_errno = errno;
+       errno = 0;
        if (!_pw_db && !__initdb())
                goto fail;
 
@@ -772,23 +790,33 @@ getpwuid_r(uid_t uid, struct passwd *pw,
                pwret = _pwhashbyuid(uid, buf, buflen, pw, flagsp);
 
        if (!_pw_stayopen) {
+               tmp_errno = errno;
                (void)(_pw_db->close)(_pw_db);
                _pw_db = NULL;
+               errno = tmp_errno;
        }
 fail:
        if (pwretp)
                *pwretp = pwret;
+       if (pwret == NULL)
+               my_errno = errno;
+       errno = saved_errno;
        _THREAD_PRIVATE_MUTEX_UNLOCK(pw);
-       return (pwret ? 0 : 1);
+       return (my_errno);
 }
 
 struct passwd *
 getpwuid(uid_t uid)
 {
        struct passwd *pw = NULL;
+       int my_errno;
 
-       if (getpwuid_r(uid, &_pw_passwd, _pw_string, sizeof _pw_string, &pw))
+       my_errno = getpwuid_r(uid, &_pw_passwd, _pw_string,
+           sizeof _pw_string, &pw);
+       if (my_errno) {
                pw = NULL;
+               errno = my_errno;
+       }
        return (pw);
 }
 
@@ -851,9 +879,12 @@ __initdb(void)
                errno = saved_errno;
                return (1);
        }
-       if (!warned)
+       if (!warned) {
+               saved_errno = errno;
                syslog(LOG_ERR, "%s: %m", _PATH_MP_DB);
-       warned = 1;
+               errno = saved_errno;
+               warned = 1;
+       }
        return (0);
 }
 
@@ -867,8 +898,10 @@ __hashpw(DBT *key, char *buf, size_t buf
        if ((_pw_db->get)(_pw_db, key, &data, 0))
                return (0);
        p = (char *)data.data;
-       if (data.size > buflen)
+       if (data.size > buflen) {
+               errno = ERANGE;
                return (0);
+       }
 
        t = buf;
 #define        EXPAND(e)       e = t; while ((*t++ = *p++));
Index: getgrent.c
===================================================================
RCS file: /cvs/src/lib/libc/gen/getgrent.c,v
retrieving revision 1.38
diff -u -p -r1.38 getgrent.c
--- getgrent.c  17 Apr 2013 17:40:35 -0000      1.38
+++ getgrent.c  22 Feb 2014 09:51:15 -0000
@@ -134,6 +134,7 @@ getgrnam_r(const char *name, struct grou
        if (bufsize < GETGR_R_SIZE_MAX)
                return ERANGE;
        errnosave = errno;
+       errno = 0;
        *result = getgrnam_gs(name, grp, (struct group_storage *)buffer);
        if (*result == NULL)
                ret = errno;
@@ -180,6 +181,7 @@ getgrgid_r(gid_t gid, struct group *grp,
        if (bufsize < GETGR_R_SIZE_MAX)
                return ERANGE;
        errnosave = errno;
+       errno = 0;
        *result = getgrgid_gs(gid, grp, (struct group_storage *)buffer);
        if (*result == NULL)
                ret = errno;

Reply via email to