Bug 1658260 - Data race bewteen setlocale/newlocale/duplocale causes valgrind to crash.
Summary: Data race bewteen setlocale/newlocale/duplocale causes valgrind to crash.
Keywords:
Status: CLOSED EOL
Alias: None
Product: Fedora
Classification: Fedora
Component: valgrind
Version: 28
Hardware: x86_64
OS: Linux
low
low
Target Milestone: ---
Assignee: Mark Wielaard
QA Contact: Fedora Extras Quality Assurance
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2018-12-11 16:09 UTC by Carlos O'Donell
Modified: 2019-05-28 19:22 UTC (History)
3 users (show)

Fixed In Version:
Doc Type: If docs needed, set a value
Doc Text:
Clone Of:
Environment:
Last Closed: 2019-05-28 19:22:25 UTC
Type: Bug
Embargoed:


Attachments (Terms of Use)


Links
System ID Private Priority Status Summary Last Updated
Sourceware 23970 0 None None None 2019-05-28 19:24:33 UTC

Description Carlos O'Donell 2018-12-11 16:09:12 UTC
Description of problem:

Running valgrind on the test program crashes valgrind.

Version-Release number of selected component (if applicable):

valgrind-3.14.0-1.fc29.x86_64

How reproducible:
cat >> test.c <<EOF
/* This test is derived from:
   https://sourceware.org/bugzilla/show_bug.cgi?id=23970
   where we are showing that setlocale, which is MT-unsafe
   creates a data-race with uselocale/duplocale/newlocale.  */
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <locale.h>
#include <pthread.h>
#include <sys/prctl.h>
#include <unistd.h>

/* Set to 1 to carry out MT-unsafe behvaiour of calling
   selocale while other threads may be accessing the global
   locale.  */
#define MTUNSAFE 1

void *
thread_none(void *arg)
{
  unsigned int flag = 1;
  char *ret;
  /* Loop forever calling setlocale, and once ever
     2^32-1 calls switch to en_US.UTF-8 just to change
     the data.  */
  while (1)
    {
#if MTUNSAFE
      if (flag != 0)
        ret = setlocale(LC_TIME, "C");
      else
	{
	  printf ("setlocale (LC_ALL, \"en_US.utf8\"");
          ret = setlocale(LC_ALL, "en_US.utf8");
	}

      if (ret == NULL)
	{
	  perror ("setlocale");
	  exit (1);
	}
#endif
      flag = flag - 1;
    }
  return NULL;
}

void *
thread_one(void *arg)
{
  locale_t newloc;
  locale_t newloc2;
  while (1)
    {
      /* Get the current locale.  */
      locale_t oldlocale = uselocale(NULL);
      /* Duplicate the current locale.  */
      locale_t duploc = duplocale(oldlocale);
      if (duploc == (locale_t) 0)
	{
	  perror ("duplocale");
	  exit (1);
	}
      /* Alter the new locale twice.  */
      newloc = newlocale(LC_NUMERIC, "C", duploc);
      if (newloc == (locale_t) 0)
	{
	  freelocale (duploc);
	  exit (1);
	}
      /* At this point duploc is in an undefined state,
	 and we must reference only newloc.  */
      newloc2 = newlocale(LC_TIME, "C", newloc);
      if (newloc == (locale_t) 0)
	{
	  /* We must free newloc if we failed the second
	     modification.  */
	  freelocale (newloc);
	  exit (1);
	}
      /* Switch to the new locale.  */
      oldlocale = uselocale(newloc2);
      if (oldlocale == (locale_t) 0)
	{
	  perror ("uselocale");
	  exit (1);
	}
      /* Free the old locale if it's not the global object.  */
      if (oldlocale != (locale_t) LC_GLOBAL_LOCALE)
        freelocale(oldlocale);
    }
  return NULL;
}

int main()
{
  pthread_t t1;
  pthread_t t2;
  pthread_t t3;
  pthread_t t4;
  pthread_t t5;

  /* Create 5 threads. */
  pthread_create(&t1, NULL, thread_none, NULL);
  pthread_create(&t2, NULL, thread_one, NULL);
  pthread_create(&t3, NULL, thread_one, NULL);
  pthread_create(&t4, NULL, thread_one, NULL);
  pthread_create(&t5, NULL, thread_one, NULL);

  /* Wait forever.  */
  while (1) {};
}
EOF
gcc -Wall -pedantic -O2 -g3 -o test test.c -lpthread

./test
malloc(): corrupted top size
Aborted (core dumped)

[carlos@athas swbz23970]$ valgrind ./test
==12548== Memcheck, a memory error detector
==12548== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==12548== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==12548== Command: ./test
==12548== 
==12548== Thread 5:
==12548== Invalid write of size 1
==12548==    at 0x483F450: __stpcpy_sse2_unaligned (vg_replace_strmem.c:1159)
==12548==    by 0x48D946F: duplocale (duplocale.c:74)
==12548==    by 0x4012D5: thread_one (test.c:58)
==12548==    by 0x488F58D: start_thread (pthread_create.c:486)
==12548==    by 0x49A66A2: clone (clone.S:95)
==12548==  Address 0x786b8e8 is 0 bytes after a block of size 232 alloc'd
==12548==    at 0x483880B: malloc (vg_replace_malloc.c:299)
==12548==    by 0x48D9431: duplocale (duplocale.c:53)
==12548==    by 0x4012D5: thread_one (test.c:58)
==12548==    by 0x488F58D: start_thread (pthread_create.c:486)
==12548==    by 0x49A66A2: clone (clone.S:95)
==12548== 
==12548== Invalid write of size 1
==12548==    at 0x483F45F: __stpcpy_sse2_unaligned (vg_replace_strmem.c:1159)
==12548==    by 0x48D946F: duplocale (duplocale.c:74)
==12548==    by 0x4012D5: thread_one (test.c:58)
==12548==    by 0x488F58D: start_thread (pthread_create.c:486)
==12548==    by 0x49A66A2: clone (clone.S:95)
==12548==  Address 0x786b8f2 is 10 bytes after a block of size 232 alloc'd
==12548==    at 0x483880B: malloc (vg_replace_malloc.c:299)
==12548==    by 0x48D9431: duplocale (duplocale.c:53)
==12548==    by 0x4012D5: thread_one (test.c:58)
==12548==    by 0x488F58D: start_thread (pthread_create.c:486)
==12548==    by 0x49A66A2: clone (clone.S:95)
==12548== 
==12548== Invalid read of size 1
==12548==    at 0x483BC26: strlen (vg_replace_strmem.c:460)
==12548==    by 0x48D8FC2: newlocale (newlocale.c:193)
==12548==    by 0x40129C: thread_one (test.c:65)
==12548==    by 0x488F58D: start_thread (pthread_create.c:486)
==12548==    by 0x49A66A2: clone (clone.S:95)
==12548==  Address 0x786b8f3 is 11 bytes after a block of size 232 alloc'd
==12548==    at 0x483880B: malloc (vg_replace_malloc.c:299)
==12548==    by 0x48D9431: duplocale (duplocale.c:53)
==12548==    by 0x4012D5: thread_one (test.c:58)
==12548==    by 0x488F58D: start_thread (pthread_create.c:486)
==12548==    by 0x49A66A2: clone (clone.S:95)
==12548== 
==12548== Invalid read of size 1
==12548==    at 0x483BC34: strlen (vg_replace_strmem.c:460)
==12548==    by 0x48D8FC2: newlocale (newlocale.c:193)
==12548==    by 0x40129C: thread_one (test.c:65)
==12548==    by 0x488F58D: start_thread (pthread_create.c:486)
==12548==    by 0x49A66A2: clone (clone.S:95)
==12548==  Address 0x786b8f4 is 12 bytes after a block of size 232 alloc'd
==12548==    at 0x483880B: malloc (vg_replace_malloc.c:299)
==12548==    by 0x48D9431: duplocale (duplocale.c:53)
==12548==    by 0x4012D5: thread_one (test.c:58)
==12548==    by 0x488F58D: start_thread (pthread_create.c:486)
==12548==    by 0x49A66A2: clone (clone.S:95)
==12548== 
==12548== Invalid read of size 1
==12548==    at 0x483F434: __stpcpy_sse2_unaligned (vg_replace_strmem.c:1159)
==12548==    by 0x48D90C8: newlocale (newlocale.c:256)
==12548==    by 0x40129C: thread_one (test.c:65)
==12548==    by 0x488F58D: start_thread (pthread_create.c:486)
==12548==    by 0x49A66A2: clone (clone.S:95)
==12548==  Address 0x786b8f3 is 11 bytes after a block of size 232 alloc'd
==12548==    at 0x483880B: malloc (vg_replace_malloc.c:299)
==12548==    by 0x48D9431: duplocale (duplocale.c:53)
==12548==    by 0x4012D5: thread_one (test.c:58)
==12548==    by 0x488F58D: start_thread (pthread_create.c:486)
==12548==    by 0x49A66A2: clone (clone.S:95)
==12548== 
==12548== Invalid read of size 1
==12548==    at 0x483F453: __stpcpy_sse2_unaligned (vg_replace_strmem.c:1159)
==12548==    by 0x48D90C8: newlocale (newlocale.c:256)
==12548==    by 0x40129C: thread_one (test.c:65)
==12548==    by 0x488F58D: start_thread (pthread_create.c:486)
==12548==    by 0x49A66A2: clone (clone.S:95)
==12548==  Address 0x786b8f4 is 12 bytes after a block of size 232 alloc'd
==12548==    at 0x483880B: malloc (vg_replace_malloc.c:299)
==12548==    by 0x48D9431: duplocale (duplocale.c:53)
==12548==    by 0x4012D5: thread_one (test.c:58)
==12548==    by 0x488F58D: start_thread (pthread_create.c:486)
==12548==    by 0x49A66A2: clone (clone.S:95)
==12548== 

valgrind: m_mallocfree.c:307 (get_bszB_as_is): Assertion 'bszB_lo == bszB_hi' failed.
valgrind: Heap block lo/hi size mismatch: lo = 304, hi = 8443777977504654592.
This is probably caused by your program erroneously writing past the
end of a heap block and corrupting heap metadata.  If you fix any
invalid writes reported by Memcheck, this assertion failure will
probably go away.  Please try that before reporting this as a bug.


host stacktrace:
==12548==    at 0x58045672: ??? (in /usr/lib64/valgrind/memcheck-amd64-linux)
==12548==    by 0x58045787: ??? (in /usr/lib64/valgrind/memcheck-amd64-linux)
==12548==    by 0x58045924: ??? (in /usr/lib64/valgrind/memcheck-amd64-linux)
==12548==    by 0x58050251: ??? (in /usr/lib64/valgrind/memcheck-amd64-linux)
==12548==    by 0x58004BFB: ??? (in /usr/lib64/valgrind/memcheck-amd64-linux)
==12548==    by 0x5800579F: ??? (in /usr/lib64/valgrind/memcheck-amd64-linux)
==12548==    by 0x580A1BE3: ??? (in /usr/lib64/valgrind/memcheck-amd64-linux)
==12548==    by 0x580EDA5A: ??? (in /usr/lib64/valgrind/memcheck-amd64-linux)
==12548==    by 0x580EDD00: ??? (in /usr/lib64/valgrind/memcheck-amd64-linux)
==12548==    by 0x580B282D: ??? (in /usr/lib64/valgrind/memcheck-amd64-linux)
==12548==    by 0xDEADBEEFDEADBEEE: ???
==12548==    by 0xDEADBEEFDEADBEEE: ???
==12548==    by 0xDEADBEEFDEADBEEE: ???

sched status:
  running_tid=5

Thread 1: status = VgTs_Yielding (lwpid 12548)
==12548==    at 0x401123: main (test.c:111)
client stack range: [0x1FFEFFD000 0x1FFF000FFF] client SP: 0x1FFEFFF3E0
valgrind stack range: [0x1002BA6000 0x1002CA5FFF] top usage: 9440 of 1048576

Thread 2: status = VgTs_WaitSys syscall 202 (lwpid 12549)
==12548==    at 0x48947C5: futex_abstimed_wait (futex-internal.h:172)
==12548==    by 0x48947C5: __pthread_rwlock_wrlock_full (pthread_rwlock_common.c:706)
==12548==    by 0x48947C5: pthread_rwlock_wrlock (pthread_rwlock_wrlock.c:27)
==12548==    by 0x48D6C8B: setlocale (setlocale.c:236)
==12548==    by 0x40123E: thread_none (test.c:30)
==12548==    by 0x488F58D: start_thread (pthread_create.c:486)
==12548==    by 0x49A66A2: clone (clone.S:95)
client stack range: [0x4A73000 0x5271FFF] client SP: 0x5271D60
valgrind stack range: [0x1005133000 0x1005232FFF] top usage: 5928 of 1048576

Thread 3: status = VgTs_WaitSys syscall 202 (lwpid 12550)
==12548==    at 0x48947C5: futex_abstimed_wait (futex-internal.h:172)
==12548==    by 0x48947C5: __pthread_rwlock_wrlock_full (pthread_rwlock_common.c:706)
==12548==    by 0x48947C5: pthread_rwlock_wrlock (pthread_rwlock_wrlock.c:27)
==12548==    by 0x48D8F86: newlocale (newlocale.c:163)
==12548==    by 0x4012B3: thread_one (test.c:73)
==12548==    by 0x488F58D: start_thread (pthread_create.c:486)
==12548==    by 0x49A66A2: clone (clone.S:95)
client stack range: [0x5674000 0x5E72FFF] client SP: 0x5E72CD0
valgrind stack range: [0x10055F7000 0x10056F6FFF] top usage: 2648 of 1048576

Thread 4: status = VgTs_WaitSys syscall 202 (lwpid 12551)
==12548==    at 0x48947C5: futex_abstimed_wait (futex-internal.h:172)
==12548==    by 0x48947C5: __pthread_rwlock_wrlock_full (pthread_rwlock_common.c:706)
==12548==    by 0x48947C5: pthread_rwlock_wrlock (pthread_rwlock_wrlock.c:27)
==12548==    by 0x48D8F86: newlocale (newlocale.c:163)
==12548==    by 0x4012B3: thread_one (test.c:73)
==12548==    by 0x488F58D: start_thread (pthread_create.c:486)
==12548==    by 0x49A66A2: clone (clone.S:95)
client stack range: [0x5E75000 0x6673FFF] client SP: 0x6673CD0
valgrind stack range: [0x1005BF0000 0x1005CEFFFF] top usage: 3048 of 1048576

Thread 5: status = VgTs_Runnable (lwpid 12552)
==12548==    at 0x483880B: malloc (vg_replace_malloc.c:299)
==12548==    by 0x48D9045: newlocale (newlocale.c:201)
==12548==    by 0x4012B3: thread_one (test.c:73)
==12548==    by 0x488F58D: start_thread (pthread_create.c:486)
==12548==    by 0x49A66A2: clone (clone.S:95)
client stack range: [0x6676000 0x6E74FFF] client SP: 0x6E74CB0
valgrind stack range: [0x1005E48000 0x1005F47FFF] top usage: 3976 of 1048576

Thread 6: status = VgTs_WaitSys syscall 202 (lwpid 12553)
==12548==    at 0x48947C5: futex_abstimed_wait (futex-internal.h:172)
==12548==    by 0x48947C5: __pthread_rwlock_wrlock_full (pthread_rwlock_common.c:706)
==12548==    by 0x48947C5: pthread_rwlock_wrlock (pthread_rwlock_wrlock.c:27)
==12548==    by 0x48D8F86: newlocale (newlocale.c:163)
==12548==    by 0x4012B3: thread_one (test.c:73)
==12548==    by 0x488F58D: start_thread (pthread_create.c:486)
==12548==    by 0x49A66A2: clone (clone.S:95)
client stack range: [0x7A77000 0x8275FFF] client SP: 0x8275CD0
valgrind stack range: [0x10068D8000 0x10069D7FFF] top usage: 2760 of 1048576


Note: see also the FAQ in the source distribution.
It contains workarounds to several common problems.
In particular, if Valgrind aborted or crashed after
identifying problems in your program, there's a good chance
that fixing those problems will prevent Valgrind aborting or
crashing, especially if it happened in m_mallocfree.c.

If that doesn't help, please report this bug to: www.valgrind.org

In the bug report, send all the above text, the valgrind
version, and what OS and version you are using.  Thanks.

[carlos@athas swbz23970]$ 

Steps to Reproduce:
1. Compile test.c
2. Run valgrind ./test

Actual results:

Valgrind reports 1-byte invalid writes in one thread, and then aborts.

Expected results:

While it is possible that the program can corrupt valgrind's state, in this case it was not something I expected. I expected Valgrind to be able to handle the overruns of the malloc's memory that were a part of the data race between setlocale and newlocale/duplocale/uselocale.

While the valgrind instructions say "Fix the Memcheck results before submitting the bug" (paraphrasing), I decided to file the bug anyway because the results were surprising for me.

Additional info:

Comment 1 Ben Cotton 2019-05-02 20:09:01 UTC
This message is a reminder that Fedora 28 is nearing its end of life.
On 2019-May-28 Fedora will stop maintaining and issuing updates for
Fedora 28. It is Fedora's policy to close all bug reports from releases
that are no longer maintained. At that time this bug will be closed as
EOL if it remains open with a Fedora 'version' of '28'.

Package Maintainer: If you wish for this bug to remain open because you
plan to fix it in a currently maintained version, simply change the 'version' 
to a later Fedora version.

Thank you for reporting this issue and we are sorry that we were not 
able to fix it before Fedora 28 is end of life. If you would still like 
to see this bug fixed and are able to reproduce it against a later version 
of Fedora, you are encouraged  change the 'version' to a later Fedora 
version prior this bug is closed as described in the policy above.

Although we aim to fix as many bugs as possible during every release's 
lifetime, sometimes those efforts are overtaken by events. Often a 
more recent Fedora release includes newer upstream software that fixes 
bugs or makes them obsolete.

Comment 2 Mark Wielaard 2019-05-06 10:09:16 UTC
This still crashes in the same way under valgrind-3.15.0

    valgrind: m_mallocfree.c:305 (get_bszB_as_is): Assertion 'bszB_lo == bszB_hi' failed.
    valgrind: Heap block lo/hi size mismatch: lo = 304, hi = 8443777977504654592.
    This is probably caused by your program erroneously writing past the
    end of a heap block and corrupting heap metadata.  If you fix any
    invalid writes reported by Memcheck, this assertion failure will
    probably go away.  Please try that before reporting this as a bug.

Given that the program writes outside allocated malloc blocks and memcheck warning about that I am not surprised.

You say:

> While it is possible that the program can corrupt valgrind's state, in this case it was not something I expected. I expected Valgrind to be able to handle the overruns of the malloc's memory that were a part of the data race between setlocale and newlocale/duplocale/uselocale.

Why did you expect this?

Comment 3 Ben Cotton 2019-05-28 19:22:25 UTC
Fedora 28 changed to end-of-life (EOL) status on 2019-05-28. Fedora 28 is
no longer maintained, which means that it will not receive any further
security or bug fix updates. As a result we are closing this bug.

If you can reproduce this bug against a currently maintained version of
Fedora please feel free to reopen this bug against that version. If you
are unable to reopen this bug, please file a new report against the
current release. If you experience problems, please add a comment to this
bug.

Thank you for reporting this bug and we are sorry it could not be fixed.


Note You need to log in before you can comment on or make changes to this bug.