summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2014-08-23 23:35:10 -0400
committerRich Felker <dalias@aerifal.cx>2014-08-23 23:35:10 -0400
commit5345c9b884e7c4e73eb2c8bb83b8d0df20f95afb (patch)
tree622716518874799c9430d2542f5d063a5906c638
parentb8ca9eb5301580dcf101753451eee196edceefbd (diff)
downloadmusl-5345c9b884e7c4e73eb2c8bb83b8d0df20f95afb.tar.gz
fix false ownership of stdio FILEs due to tid reuse
this is analogous commit fffc5cda10e0c5c910b40f7be0d4fa4e15bb3f48 which fixed the corresponding issue for mutexes. the robust list can't be used here because the locks do not share a common layout with mutexes. at some point it may make sense to simply incorporate a mutex object into the FILE structure and use it, but that would be a much more invasive change, and it doesn't mesh well with the current design that uses a simpler code path for internal locking and pulls in the recursive-mutex-like code when the flockfile API is used explicitly.
-rw-r--r--src/internal/pthread_impl.h1
-rw-r--r--src/internal/stdio_impl.h1
-rw-r--r--src/stdio/fclose.c6
-rw-r--r--src/stdio/ftrylockfile.c22
-rw-r--r--src/stdio/funlockfile.c10
-rw-r--r--src/thread/pthread_create.c2
6 files changed, 40 insertions, 2 deletions
diff --git a/src/internal/pthread_impl.h b/src/internal/pthread_impl.h
index 4de66378..74c62cce 100644
--- a/src/internal/pthread_impl.h
+++ b/src/internal/pthread_impl.h
@@ -44,6 +44,7 @@ struct pthread {
int exitlock[2];
int startlock[2];
unsigned long sigmask[_NSIG/8/sizeof(long)];
+ void *stdio_locks;
};
struct __timer {
diff --git a/src/internal/stdio_impl.h b/src/internal/stdio_impl.h
index 79be9fdb..d659522f 100644
--- a/src/internal/stdio_impl.h
+++ b/src/internal/stdio_impl.h
@@ -46,6 +46,7 @@ struct _IO_FILE {
void *mustbezero_2;
unsigned char *shend;
off_t shlim, shcnt;
+ FILE *prev_locked, *next_locked;
};
size_t __stdio_read(FILE *, unsigned char *, size_t);
diff --git a/src/stdio/fclose.c b/src/stdio/fclose.c
index 38e8a1e3..317b3c90 100644
--- a/src/stdio/fclose.c
+++ b/src/stdio/fclose.c
@@ -1,4 +1,8 @@
#include "stdio_impl.h"
+#include "libc.h"
+
+static void dummy(FILE *f) { }
+weak_alias(dummy, __unlist_locked_file);
int fclose(FILE *f)
{
@@ -7,6 +11,8 @@ int fclose(FILE *f)
FFINALLOCK(f);
+ __unlist_locked_file(f);
+
if (!(perm = f->flags & F_PERM)) {
OFLLOCK();
if (f->prev) f->prev->next = f->next;
diff --git a/src/stdio/ftrylockfile.c b/src/stdio/ftrylockfile.c
index 56cccafd..6f9a4b88 100644
--- a/src/stdio/ftrylockfile.c
+++ b/src/stdio/ftrylockfile.c
@@ -2,9 +2,26 @@
#include "pthread_impl.h"
#include <limits.h>
+void __do_orphaned_stdio_locks()
+{
+ FILE *f;
+ for (f=__pthread_self()->stdio_locks; f; f=f->next_locked)
+ a_store(&f->lock, 0x40000000);
+}
+
+void __unlist_locked_file(FILE *f)
+{
+ if (f->lockcount) {
+ if (f->next_locked) f->next_locked->prev_locked = f->prev_locked;
+ if (f->prev_locked) f->prev_locked->next_locked = f->next_locked;
+ else __pthread_self()->stdio_locks = f->next_locked;
+ }
+}
+
int ftrylockfile(FILE *f)
{
- int tid = __pthread_self()->tid;
+ pthread_t self = __pthread_self();
+ int tid = self->tid;
if (f->lock == tid) {
if (f->lockcount == LONG_MAX)
return -1;
@@ -15,5 +32,8 @@ int ftrylockfile(FILE *f)
if (f->lock || a_cas(&f->lock, 0, tid))
return -1;
f->lockcount = 1;
+ f->prev_locked = 0;
+ f->next_locked = self->stdio_locks;
+ self->stdio_locks = f;
return 0;
}
diff --git a/src/stdio/funlockfile.c b/src/stdio/funlockfile.c
index f8a2a071..30a07ef4 100644
--- a/src/stdio/funlockfile.c
+++ b/src/stdio/funlockfile.c
@@ -1,7 +1,15 @@
#include "stdio_impl.h"
#include "pthread_impl.h"
+void __unlist_locked_file(FILE *);
+
void funlockfile(FILE *f)
{
- if (!--f->lockcount) __unlockfile(f);
+ if (f->lockcount == 1) {
+ __unlist_locked_file(f);
+ f->lockcount = 0;
+ __unlockfile(f);
+ } else {
+ f->lockcount--;
+ }
}
diff --git a/src/thread/pthread_create.c b/src/thread/pthread_create.c
index 1601614a..e441bdac 100644
--- a/src/thread/pthread_create.c
+++ b/src/thread/pthread_create.c
@@ -12,6 +12,7 @@ weak_alias(dummy_0, __acquire_ptc);
weak_alias(dummy_0, __release_ptc);
weak_alias(dummy_0, __pthread_tsd_run_dtors);
weak_alias(dummy_0, __do_private_robust_list);
+weak_alias(dummy_0, __do_orphaned_stdio_locks);
_Noreturn void pthread_exit(void *result)
{
@@ -66,6 +67,7 @@ _Noreturn void pthread_exit(void *result)
}
__do_private_robust_list();
+ __do_orphaned_stdio_locks();
if (self->detached && self->map_base) {
/* Detached threads must avoid the kernel clear_child_tid