While running tests (for ECPG in Postgres), we're getting occasional segfaults in tests using threads. This seems to affect only the AArch64 machine (which is an rpi5), while the two x86_64 machines don't seem to have this issue. The crashes generally look like this: ------ Program terminated with signal SIGSEGV, Segmentation fault. Address not mapped to object. #0 __vfprintf (fp=fp@entry=0x889dab00, locale=locale@entry=0x841dfcf0 <__xlocale_global_locale>, serrno=0, fmt0=fmt0@entry=0x20087a "Connection: %d", ap=...) at /usr/src/lib/libc/stdio/vfprintf.c:477 warning: 477 /usr/src/lib/libc/stdio/vfprintf.c: No such file or directory [Current thread is 1 (LWP 933707)] (gdb) bt #0 __vfprintf (fp=fp@entry=0x889dab00, locale=locale@entry=0x841dfcf0 <__xlocale_global_locale>, serrno=0, fmt0=fmt0@entry=0x20087a "Connection: %d", ap=...) at /usr/src/lib/libc/stdio/vfprintf.c:477 #1 0x000000008411d434 in vsprintf_l (locale=0x841dfcf0 <__xlocale_global_locale>, ap=..., str=<optimized out>, fmt=<optimized out>) at /usr/src/lib/libc/stdio/vsprintf.c:61 #2 vsprintf (str=0x889daefc "", fmt=<optimized out>, ap=...) at /usr/src/lib/libc/stdio/vsprintf.c:68 #3 0x00000000841123f4 in sprintf (str=0x0, str@entry=0x889daefc "", fmt=0x841dfcf0 <__xlocale_global_locale> "") at /usr/src/lib/libc/stdio/sprintf.c:56 #4 0x0000000000210ed8 in fn (arg=0x0) at prep.pgc:39 #5 0x00000000847844fc in thread_start (curthread=0x63044546d010) at /usr/src/lib/libthr/thread/thr_create.c:291 #6 0x0000000000000000 in ?? () ------ That is, it's a crash in vfprintf, on this line: decpt_len = (decimal_point[1] == '\0' ? 1 : strlen(decimal_point)); After investigating a bit, I believe this is due to localeconv_l using atomics in relaxed mode when updating the decimal_point: if (atomic_load_acq_int(&loc->numeric_locale_changed) != 0) { ... N_ASSIGN_STR(decimal_point); ... atomic_store_int(&loc->numeric_locale_changed, 0); } On architectures with weakly-ordered memory model (like the AArch64), this means the other threads may see the update to numeric_locale_changed, without seeing the updated decimal_point pointer. Which leads to the crash. This can be reproduced by a simple program: ------ #include <stdio.h> #include <pthread.h> void *fn(void *arg) { char buf[128]; for(int i=0; i<10000; i++) sprintf(buf, "%d", 1); return NULL; } int main() { pthread_t th[100]; for(int i=0; i<100; i++) pthread_create(&th[i], NULL, fn, NULL); for(int i=0; i<100; i++) pthread_join(th[i], NULL); return 0; } ------ It's a race condition, so it may not crash. But for me it usually takes only ~10-20 tries to crash with a segfault. I believe the code should use atomic_store_rel_int instead, to get the proper memory ordering guarantees. Not just for decimal_point, but for the other fields too. (I haven't looked at other places using the relaxed atomics variants, but there might be more similar bugs there.) In practice, this race condition may be fairly unlikely to hit - the window is fairly narrow. And it's probably enough for the "main" process to do a single sprintf (which initializes the cache) before starting the threads. Which our tests don't do, but bigger apps probably do. Adding such sprintf call to the reproducer seems to fix it.
It is easy to change to store_rel in lib/libc/locale/localeconv.c, but is it enough? Suppose that we did not observed numeric_locale_changed != 0, but did observed new values from some loaded fields. Could this inconsistency cause issues? If yes, we need something like seqlocks, as used by timecounters and filedescriptors.
(In reply to Konstantin Belousov from comment #1) I'm not very familiar with how the locales work, but the way I understood localeconv_l is that it rebuilds the cached fields after a change (or on first access). And I thought maybe it's harmless if two threads rebuilt it concurrently, or something like that. But maybe you're right and after a locale change a thread could see a mix of old and new values for a given locale? I guess that'd mean using atomics for protecting other fields (as implemented here) is not quite sufficient.
(In reply to Tomas Vondra from comment #2) I am not sure. For instance one thread might have noticed the change in the locale and started the rebuild, while other did not and load the partially updated data. Anyway, I think that the change to use the release semantic is fine on its own.
A commit in branch main references this bug: URL: https://cgit.FreeBSD.org/src/commit/?id=4efbcf36a0d49ab142023a767871532f515f1381 commit 4efbcf36a0d49ab142023a767871532f515f1381 Author: Konstantin Belousov <kib@FreeBSD.org> AuthorDate: 2026-07-05 02:50:27 +0000 Commit: Konstantin Belousov <kib@FreeBSD.org> CommitDate: 2026-07-05 03:18:50 +0000 libc locale/localeconv.c: use release semantic when clearing locale_changed PR: 296410 Submitted by: Tomas Vondra <tomas@vondra.me> MFC after: 1 week lib/libc/locale/localeconv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
A commit in branch stable/15 references this bug: URL: https://cgit.FreeBSD.org/src/commit/?id=b34fd6017a572b3f62885cc8976df7c2449834cf commit b34fd6017a572b3f62885cc8976df7c2449834cf Author: Konstantin Belousov <kib@FreeBSD.org> AuthorDate: 2026-07-05 02:50:27 +0000 Commit: Konstantin Belousov <kib@FreeBSD.org> CommitDate: 2026-07-12 08:34:45 +0000 libc locale/localeconv.c: use release semantic when clearing locale_changed PR: 296410 (cherry picked from commit 4efbcf36a0d49ab142023a767871532f515f1381) lib/libc/locale/localeconv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
Do I understand correctly the fix went only to main, and not to the stable (14 and 15) branches? If that's the case, we'll probably need to apply some sort of workaround on our side, to prevent the failures on the older releases.
(In reply to Tomas Vondra from comment #6) Ah, damn. Now I noticed the second commit in stable/15. Still, my test machine is running 14.4, so that may still need the workaround.
A commit in branch stable/14 references this bug: URL: https://cgit.FreeBSD.org/src/commit/?id=00e880819cc5b8249222792ec82c45444a50eb2d commit 00e880819cc5b8249222792ec82c45444a50eb2d Author: Konstantin Belousov <kib@FreeBSD.org> AuthorDate: 2026-07-05 02:50:27 +0000 Commit: Konstantin Belousov <kib@FreeBSD.org> CommitDate: 2026-07-15 13:36:50 +0000 libc locale/localeconv.c: use release semantic when clearing locale_changed PR: 296410 (cherry picked from commit 4efbcf36a0d49ab142023a767871532f515f1381) lib/libc/locale/localeconv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
^Triage: committed and (partially) MFCed.