Page MenuHome GnuPG

Provide more practical thread-safe strerror, perhaps with strerror_l
Open, NormalPublic

Description

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

I find it quite difficult to use strerror_r and gpg_strerror_r. With having to guess and retry to get an appropriate buffer length, a wrapper which dynamically allocates the string seems to be needed.

Owing partly to this but primarily to GNU's historical version that worked differently, POSIX Issue 8 is slated to recommend strerror_l for most uses. This works like strerror in that it takes care of automatic allocation, but uses thread-local storage and should be suitable the vast majority of the time.
-----BEGIN PGP SIGNATURE-----

iHUEARYIAB0WIQT287WtmxUhmhucNnhyvHFIwKstpwUCX6/1HQAKCRByvHFIwKst
p5ZKAP0RTATVpdpve94pSm9d113CzQ3EZyPfYgOZGTkPnzLZKgEAvuoArkexKgUV
HtqzptrMnw08rss0aEqXs7NqwZ71Mgk=
=98lC
-----END PGP SIGNATURE-----

Event Timeline

I know these troubles.

The problem is that we use strerror_r in the supplementation for system error messages. All other errors are static strings and thus gpg_strerror returns a const char *. The latter is of course not fully correct because a system error might be from a buffer "unknown error NNN". IIRC, we defined the function this way because we planned to also return static strings for system errors. Maybe due to the fact that we would need to translate them all, this has not been done. And to a minor part because our translations might then be different than the system provided translations.

Using strerror_l along with a macro indicating MT safeness would indeed be a way to go without getting into the trouble of using TLS on many platforms. For Windows we could do that easily, though.

werner triaged this task as Normal priority.Nov 16 2020, 9:08 AM

Actually, I think there's a way to make gpg_strerror_r more usable on its own. I previously said

I find it quite difficult to use strerror_r and gpg_strerror_r. With having to guess and retry to get an appropriate buffer length, a wrapper which dynamically allocates the string seems to be needed.

Well, it turns out I was wrong. POSIX prescribes in limits.h a limit, NL_TEXTMAX, that is the largest length of a message string (including the strings returned by the strerror family of functions). Unlike most of the other limits POSIX prescribes, NL_TEXTMAX is not allowed to be indeterminate and must be defined (so there is no corresponding _SC_NL_TEXTMAX). So this is a buffer size that can be used with strerror_r that should always be sufficient. (The GNU C Library is non-conforming in its not defining NL_TEXTMAX, but it is somewhat well-known that with glibc, a 1024-byte buffer always suffices.)

So I see a couple ways that gpg_strerror_r could be made more usable. One is to extend strerror_r's guarantee: gpgrt's documentation could explicitly state that when NL_TEXTMAX is defined by the system, then this buffer size is always sufficient. Alternatively, gpgrt could define a constant like GPG_STRERROR_MAX that is synonymous with NL_TEXTMAX (or is 1024 for glibc) when that size suffices, or if one of GPG's strings is longer, be defined to that upper limit.

Note that this issue is a little less pressing than it was: on Windows, Musl libc, and very recent versions of glibc, strerror() is thread-safe and this seems to be a trend.