standards/124860: flockfile(3) doesn't work when the memory has been exhausted.

Tomohisa Tanaka syl at ash.kaz.appi.keio.ac.jp
Sat Jun 21 23:00:14 UTC 2008


>Number:         124860
>Category:       standards
>Synopsis:       flockfile(3) doesn't work when the memory has been exhausted.
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-standards
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sat Jun 21 23:00:13 UTC 2008
>Closed-Date:
>Last-Modified:
>Originator:     Tomohisa Tanaka
>Release:        FreeBSD 6.3-RELEASE i386
>Organization:
>Environment:
System: FreeBSD freebsd.localdomain 6.3-RELEASE FreeBSD 6.3-RELEASE #0: Wed Jan 16 04:18:52 UTC 2008 root at dessler.cse.buffalo.edu:/usr/obj/usr/src/sys/GENERIC i386

>Description:
	When the flockfile(3) acquires a lock, and the specified
	stream has never locked, and the memory has been exhausted,
	flockfile(3) returns without the stream locked.

	The flockfile(3) calls _pthread_mutex_lock() according to the
	file /usr/src/lib/libc/stdio/_flock_stub.c, as follows.

/usr/src/lib/libc/stdio/_flock_stub.c:
     69 #define _lock _extra
     ...
     83                 _pthread_mutex_lock(&fp->_lock->fl_mutex);

        The _pthread_mutex_lock() does never fail if
        fp->_extra->fl_mutex has already been dynamically initialized.
        However, if fp->_extra->fl_mutex has been statically
        initialized, the _pthread_mutex_lock() tries to perform the
        dynamic initialization of it by calling the
        pthread_mutex_init(3), as follows.

/usr/src/lib/libpthread/thread/thr_mutex.c:
    302 static int
    303 init_static_private(struct pthread *thread, pthread_mutex_t *mutex)
    304 {
        ...
    310                 ret = pthread_mutex_init(mutex, &static_mattr);
        ...
    317 }
        ...
    849 int
    850 _pthread_mutex_lock(pthread_mutex_t *m)
    851 {
        ...
    866         else if ((*m != NULL) ||
    867             ((ret = init_static_private(curthread, m)) == 0))
    868                 ret = mutex_lock_common(curthread, m, NULL);
        ...
    871 }

    	When the memory has been exhausted, the pthread_mutex_init(3)
    	returns ENOMEM.  All mutexes of the stdio stream are
    	statically initialized, according to the file
    	/usr/src/lib/libc/stdio/findfp.c.

>How-To-Repeat:
% cat test.c
#include <stdio.h>
#include <stdlib.h>
#include <err.h>
#include <errno.h>
#include <pthread.h>

static pthread_barrier_t barrier;
static FILE *fp;

static void *
run(void *arg)
{
  pthread_barrier_wait(&barrier);
  flockfile(fp); /* [1] */
  return NULL;
}

int
main(void)
{
  pthread_t thread;
  pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
  void *p;
  size_t k;

  if (pthread_barrier_init(&barrier, NULL, 2) != 0)
    err(1, "pthread_barrier_init");
  if (pthread_create(&thread, NULL, run, NULL) != 0)
    err(1, "pthread_create");
  if ((fp = fopen("log", "w")) == NULL)
    err(1, "log");

#if 1
  for (k = 1; k < 64; ++k) {
    while ((p = malloc(k)) != NULL)
      ;
  }
  /*
   * Now, we are in a special situation where the memory has been
   * extremely exhausted.  Here, 'mutex' has not initialized with
   * pthread_mutex_init(3), so pthread_mutex_lock(3) as follows will
   * try to initialize "mutex" using pthread_mutex_init(3) internally.
   * However, malloc(3) within the pthread_mutex_init(3) returns NULL,
   * and then pthread_mutex_lock(3) fails and returns ENOMEM.
   */
  if ((errno = pthread_mutex_lock(&mutex)) != 0) {
    warn("pthread_mutex_lock");
  }
#endif

  flockfile(fp); /* [2] */
  /*
   * Here, this main thread does NOT have the lock of "fp", because
   * pthread_mutex_lock(3) within flockfile(3) at [2] has failed.  We
   * must remember "fp->_extra->fl_mutex" has not been initialized by
   * pthread_mutex_init(3) as well as "mutex".
   */
  pthread_barrier_wait(&barrier);
  /*
   * Another thread will call flockfile(3) at [1], but it returns
   * immediately.  No thread can get the lock of 'fp' in this
   * situation.  If we comment out from "#if 1" to "#endif", a
   * deadlock occurs.
   */
  pthread_join(thread, NULL);
  return 0;
}

% cat test.sh
#!/bin/sh
ulimit -v 10000
./a.out

% gcc -pthread test.c 
% sh test.sh  
a.out: pthread_mutex_lock: Cannot allocate memory 
% 

>Fix:
	I think that the function __sfp() of
	/usr/src/lib/libc/stdio/findfp.c must perform the dynamic
	initialization of fp->_extra->fl_mutex, if it has been only
	statically initialized.  And if it failed, __sfp() must return
	NULL.
>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-standards mailing list