This is an archive of the discontinued LLVM Phabricator instance.

Make system_error::message() thread safe. Fixes PR25598.
ClosedPublic

Authored by EricWF on Jun 2 2016, 3:29 AM.

Details

Summary

system_error::message() uses strerror for the generic and system categories. This function is not thread safe.

The fix is to use strerror_r. It has been available since 2001 for GNU libc and since BSD 4.4 on FreeBSD/OS X.
On platforms with GNU libc the extended version is used which always returns a valid string, even if an error occurs.

In single-threaded builds strerror is still used.

See https://llvm.org/bugs/show_bug.cgi?id=25598

Diff Detail

Event Timeline

EricWF updated this revision to Diff 59356.Jun 2 2016, 3:29 AM
EricWF retitled this revision from to Make system_error::message() thread safe. Fixes PR25598..
EricWF updated this object.
EricWF added reviewers: mclow.lists, majnemer.
EricWF added a subscriber: cfe-commits.
mclow.lists edited edge metadata.Jun 2 2016, 8:22 AM

In general, I'm OK with this - but I'm concerned about that there's not really any provision for the case where strerror_r does not exist.

Also, there's no reason to have a thread local static array here, if you're going to immediately copy it into a std::string.

The C++ spec states that error_category::message() shall not change the value of errno (See section 19.5). So errno will have to be saved and restored if strerror_r() fails.

The POSIX version of strerror_r() returns 0 on success, and any other value indicates an error (Reference).

The function may return uninitialized memory if std::snprintf() fails.

You may want to a version for Windows that calls strerror_s().

In general, I'm OK with this - but I'm concerned about that there's not really any provision for the case where strerror_r does not exist.

Also, there's no reason to have a thread local static array here, if you're going to immediately copy it into a std::string.

Ack. I changed the arrays to be neither static nor thread_local.

The C++ spec states that error_category::message() shall not change the value of errno (See section 19.5). So errno will have to be saved and restored if strerror_r() fails.

Good catch. I only have to do this for the POSIX version. I'll make the change.

The POSIX version of strerror_r() returns 0 on success, and any other value indicates an error (Reference).

The function may return uninitialized memory if std::snprintf() fails.

I don't see how snprintf could possible fail.

You may want to a version for Windows that calls strerror_s().

Libc++ provides no windows support.

EricWF updated this revision to Diff 60642.Jun 13 2016, 7:44 PM
EricWF edited edge metadata.
  • Use a local buffer instead of a static thread_local one.
  • Save and restore errno in the POSIX implementation.
mclow.lists accepted this revision.Jun 13 2016, 8:45 PM
mclow.lists edited edge metadata.

LGTM.

This revision is now accepted and ready to land.Jun 13 2016, 8:45 PM
EricWF updated this revision to Diff 60649.Jun 13 2016, 8:50 PM
EricWF edited edge metadata.

Use named variable strerror_buff_size variable instead of literal 1024.

EricWF closed this revision.Jun 13 2016, 8:52 PM

The POSIX version of strerror_r() returns 0 on success, and any other value indicates an error (Reference).

I should have been more explicit when I wrote this recommendation. Checking if strerror_r returns -1 is not portable. For example, OSX and FreeBSD return an error code from strerror_r without setting errno. The POSIX version should check for success by checking to see if strerror_r returns 0. If the function fails and the return value is not -1, assume that the return value is an error code.

The POSIX version of strerror_r() returns 0 on success, and any other value indicates an error (Reference).

I should have been more explicit when I wrote this recommendation. Checking if strerror_r returns -1 is not portable. For example, OSX and FreeBSD return an error code from strerror_r without setting errno. The POSIX version should check for success by checking to see if strerror_r returns 0. If the function fails and the return value is not -1, assume that the return value is an error code.

Urg IDK how I missed that in the docs. Thanks for pointing this out.

I fixed it in r272640. I'll follow up with some tests for the FreeBSD and OS X implementations.

I checked in tests for the reported bug in r272642. They aren't the most portable so hopefully the pass on all supported platforms.

I don't see any other issues. Thanks for fixing this.