From owner-freebsd-standards@FreeBSD.ORG Sat Jun 21 23:00:14 2008 Return-Path: Delivered-To: freebsd-standards@hub.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 721C1106566B for ; Sat, 21 Jun 2008 23:00:14 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (freefall.freebsd.org [IPv6:2001:4f8:fff6::28]) by mx1.freebsd.org (Postfix) with ESMTP id 56B148FC0A for ; Sat, 21 Jun 2008 23:00:14 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (gnats@localhost [127.0.0.1]) by freefall.freebsd.org (8.14.2/8.14.2) with ESMTP id m5LN0EET057465 for ; Sat, 21 Jun 2008 23:00:14 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.14.2/8.14.1/Submit) id m5LN0Eso057464; Sat, 21 Jun 2008 23:00:14 GMT (envelope-from gnats) Resent-Date: Sat, 21 Jun 2008 23:00:14 GMT Resent-Message-Id: <200806212300.m5LN0Eso057464@freefall.freebsd.org> Resent-From: FreeBSD-gnats-submit@FreeBSD.org (GNATS Filer) Resent-To: freebsd-standards@FreeBSD.org Resent-Reply-To: FreeBSD-gnats-submit@FreeBSD.org, Tomohisa Tanaka Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 114C51065672 for ; Sat, 21 Jun 2008 22:59:47 +0000 (UTC) (envelope-from syl@ash.kaz.appi.keio.ac.jp) Received: from ash.kaz.appi.keio.ac.jp (ash.kaz.appi.keio.ac.jp [131.113.64.54]) by mx1.freebsd.org (Postfix) with ESMTP id BD0608FC0A for ; Sat, 21 Jun 2008 22:59:46 +0000 (UTC) (envelope-from syl@ash.kaz.appi.keio.ac.jp) Received: from ash.kaz.appi.keio.ac.jp (localhost [127.0.0.1]) by ash.kaz.appi.keio.ac.jp (8.13.8/8.13.8) with ESMTP id m5LN3TlK074704 for ; Sun, 22 Jun 2008 08:03:29 +0900 (JST) (envelope-from syl@ash.kaz.appi.keio.ac.jp) Received: (from syl@localhost) by ash.kaz.appi.keio.ac.jp (8.13.8/8.13.8/Submit) id m5LN3TR6074703; Sun, 22 Jun 2008 08:03:29 +0900 (JST) (envelope-from syl) Message-Id: <200806212303.m5LN3TR6074703@ash.kaz.appi.keio.ac.jp> Date: Sun, 22 Jun 2008 08:03:29 +0900 (JST) From: Tomohisa Tanaka To: FreeBSD-gnats-submit@FreeBSD.org X-Send-Pr-Version: 3.113 Cc: Subject: standards/124860: flockfile(3) doesn't work when the memory has been exhausted. X-BeenThere: freebsd-standards@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list Reply-To: Tomohisa Tanaka List-Id: Standards compliance List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 21 Jun 2008 23:00:14 -0000 >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@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 #include #include #include #include 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: