Mercurial > hg > octave-lojdl > gnulib-hg
diff lib/strerror_r.c @ 14815:820329b2a895
strerror_r: enforce POSIX recommendations
POSIX recommends (but does not require) that strerror_r populate
buf even on error. But since we guarantee this behavior for
strerror, we might as well also guarantee it for strerror_r.
* lib/strerror_r.c (safe_copy): New helper method.
(strerror_r): Guarantee a non-empty string.
* tests/test-strerror_r.c (main): Enhance tests to incorporate
recent POSIX rulings and to match our strerror guarantees.
* doc/posix-functions/strerror_r.texi (strerror_r): Document this.
Signed-off-by: Eric Blake <eblake@redhat.com>
author | Eric Blake <eblake@redhat.com> |
---|---|
date | Wed, 18 May 2011 15:19:51 -0600 |
parents | fedc69ad1054 |
children | b6363790caec |
line wrap: on
line diff
--- a/lib/strerror_r.c +++ b/lib/strerror_r.c @@ -92,6 +92,30 @@ #endif +/* Copy as much of MSG into BUF as possible, without corrupting errno. + Return 0 if MSG fit in BUFLEN, otherwise return ERANGE. */ +static int +safe_copy (char *buf, size_t buflen, const char *msg) +{ + size_t len = strlen (msg); + int ret; + + if (len < buflen) + { + /* Although POSIX allows memcpy() to corrupt errno, we don't + know of any implementation where this is a real problem. */ + memcpy (buf, msg, len + 1); + ret = 0; + } + else + { + memcpy (buf, msg, buflen - 1); + buf[buflen - 1] = '\0'; + ret = ERANGE; + } + return ret; +} + int strerror_r (int errnum, char *buf, size_t buflen) @@ -102,9 +126,10 @@ if (buflen <= 1) { if (buflen) - *buf = 0; + *buf = '\0'; return ERANGE; } + *buf = '\0'; #if GNULIB_defined_ETXTBSY \ || GNULIB_defined_ESOCK \ @@ -413,19 +438,7 @@ } if (msg) - { - int saved_errno = errno; - size_t len = strlen (msg); - int ret = ERANGE; - - if (len < buflen) - { - memcpy (buf, msg, len + 1); - ret = 0; - } - errno = saved_errno; - return ret; - } + return safe_copy (buf, buflen, msg); } #endif @@ -441,6 +454,13 @@ ret = __xpg_strerror_r (errnum, buf, buflen); if (ret < 0) ret = errno; + if (!*buf) + { + /* glibc 2.13 would not touch buf on err, so we have to fall + back to GNU strerror_r which always returns a thread-safe + untruncated string to (partially) copy into our buf. */ + safe_copy (buf, buflen, strerror_r (errnum, buf, buflen)); + } } #elif USE_SYSTEM_STRERROR_R @@ -453,18 +473,11 @@ { char stackbuf[80]; - if (buflen < sizeof (stackbuf)) + if (buflen < sizeof stackbuf) { - ret = strerror_r (errnum, stackbuf, sizeof (stackbuf)); + ret = strerror_r (errnum, stackbuf, sizeof stackbuf); if (ret == 0) - { - size_t len = strlen (stackbuf); - - if (len < buflen) - memcpy (buf, stackbuf, len + 1); - else - ret = ERANGE; - } + ret = safe_copy (buf, buflen, stackbuf); } else ret = strerror_r (errnum, buf, buflen); @@ -479,19 +492,7 @@ /* FreeBSD rejects 0; see http://austingroupbugs.net/view.php?id=382. */ if (errnum == 0 && ret == EINVAL) - { - if (buflen <= strlen ("Success")) - { - ret = ERANGE; - if (buflen) - buf[0] = 0; - } - else - { - ret = 0; - strcpy (buf, "Success"); - } - } + ret = safe_copy (buf, buflen, "Success"); #else /* USE_SYSTEM_STRERROR */ @@ -528,17 +529,7 @@ if (errmsg == NULL || *errmsg == '\0') ret = EINVAL; else - { - size_t len = strlen (errmsg); - - if (len < buflen) - { - memcpy (buf, errmsg, len + 1); - ret = 0; - } - else - ret = ERANGE; - } + ret = safe_copy (buf, buflen, errmsg); # if HAVE_CATGETS && (defined __NetBSD__ || defined __hpux) if (catd != (nl_catd)-1) catclose (catd); @@ -558,17 +549,7 @@ if (errmsg == NULL || *errmsg == '\0') ret = EINVAL; else - { - size_t len = strlen (errmsg); - - if (len < buflen) - { - memcpy (buf, errmsg, len + 1); - ret = 0; - } - else - ret = ERANGE; - } + ret = safe_copy (buf, buflen, errmsg); } else ret = EINVAL; @@ -586,17 +567,7 @@ if (errmsg == NULL || *errmsg == '\0') ret = EINVAL; else - { - size_t len = strlen (errmsg); - - if (len < buflen) - { - memcpy (buf, errmsg, len + 1); - ret = 0; - } - else - ret = ERANGE; - } + ret = safe_copy (buf, buflen, errmsg); } gl_lock_unlock (strerror_lock); @@ -605,6 +576,9 @@ #endif + if (ret == EINVAL && !*buf) + snprintf (buf, buflen, "Unknown error %d", errnum); + errno = saved_errno; return ret; }