File: thread-safety.patch

package info (click to toggle)
libeatmydata 105-9
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 2,136 kB
  • sloc: sh: 12,307; ansic: 756; makefile: 105
file content (196 lines) | stat: -rw-r--r-- 6,850 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
From 72a074fde1b722a3e784a3648a34cd58c3196598 Mon Sep 17 00:00:00 2001
From: Alberto Bertogli <albertito@blitiri.com.ar>
Date: Mon, 28 Jan 2019 15:39:31 +0000
Subject: [PATCH] Make initialization thread-safe, and support early callers

The current initialization code is not thread-safe, and assumes that the
only possible caller of open() before initialization completes is
dlsym(), which is not the case if there are other preloaded libraries.

This patch makes the initialization thread-safe by using thread-local
variables, and adjusts the recursive checking logic to support early
callers.

Bug-Debian: https://bugs.debian.org/918520
Forwarded: https://github.com/stewartsmith/libeatmydata/pull/19
---
 configure.ac                |  2 +
 libeatmydata/libeatmydata.c | 39 ++++++++++---------
 m4/ax_tls.m4                | 74 +++++++++++++++++++++++++++++++++++++
 3 files changed, 98 insertions(+), 17 deletions(-)
 create mode 100644 m4/ax_tls.m4

--- a/configure.ac
+++ b/configure.ac
@@ -55,6 +55,8 @@
 AC_CHECK_DECLS(sync_file_range)
 AC_CHECK_FUNCS(sync_file_range)
 
+AX_TLS(:,:)
+
 AC_CONFIG_FILES(Makefile docs/header.html libeatmydata.spec)
 
 AC_OUTPUT
--- a/libeatmydata/libeatmydata.c
+++ b/libeatmydata/libeatmydata.c
@@ -48,14 +48,18 @@
 typedef int (*libc_sync_file_range_t)(int, off64_t, off64_t, unsigned int);
 #endif
 
-static libc_open_t libc_open= NULL;
-static libc_open64_t libc_open64= NULL;
-static libc_fsync_t libc_fsync= NULL;
-static libc_sync_t libc_sync= NULL;
-static libc_fdatasync_t libc_fdatasync= NULL;
-static libc_msync_t libc_msync= NULL;
+/* All the following are thread-local, to avoid initialization races between
+ * threads. */
+static TLS int init_running = 0;
+static TLS int init_complete = 0;
+static TLS libc_open_t libc_open= NULL;
+static TLS libc_open64_t libc_open64= NULL;
+static TLS libc_fsync_t libc_fsync= NULL;
+static TLS libc_sync_t libc_sync= NULL;
+static TLS libc_fdatasync_t libc_fdatasync= NULL;
+static TLS libc_msync_t libc_msync= NULL;
 #ifdef HAVE_SYNC_FILE_RANGE
-static libc_sync_file_range_t libc_sync_file_range= NULL;
+static TLS libc_sync_file_range_t libc_sync_file_range= NULL;
 #endif
 
 #define ASSIGN_DLSYM_OR_DIE(name)			\
@@ -70,13 +74,12 @@
 #pragma weak pthread_testcancel
 
 int LIBEATMYDATA_API msync(void *addr, size_t length, int flags);
-static int initing = 0;
 
 void __attribute__ ((constructor)) eatmydata_init(void);
 
 void __attribute__ ((constructor)) eatmydata_init(void)
 {
-	initing = 1;
+	init_running++;
 	ASSIGN_DLSYM_OR_DIE(open);
 	ASSIGN_DLSYM_OR_DIE(open64);
 	ASSIGN_DLSYM_OR_DIE(fsync);
@@ -86,13 +89,14 @@
 #ifdef HAVE_SYNC_FILE_RANGE
 	ASSIGN_DLSYM_IF_EXIST(sync_file_range);
 #endif
-	initing = 0;
+	init_running--;
+	init_complete++;
 }
 
 static int eatmydata_is_hungry(void)
 {
 	/* Init here, as it is called before any libc functions */
-	if(!libc_open)
+	if(!init_complete)
 		eatmydata_init();
 
 #ifdef CHECK_FILE
@@ -149,9 +153,9 @@
 #endif
 	va_end(ap);
 
-	/* In pthread environments the dlsym() may call our open(). */
-	/* We simply ignore it because libc is already loaded       */
-	if (initing) {
+	/* If we get called recursively during initialization (which should
+	 * be rare but might happen), just fail. */
+	if (init_running > 0) {
 		errno = EFAULT;
 		return -1;
 	}
@@ -176,9 +180,9 @@
 #endif
 	va_end(ap);
 
-	/* In pthread environments the dlsym() may call our open(). */
-	/* We simply ignore it because libc is already loaded       */
-	if (initing) {
+	/* If we get called recursively during initialization (which should
+	 * be rare but might happen), just fail. */
+	if (init_running > 0) {
 		errno = EFAULT;
 		return -1;
 	}
--- /dev/null
+++ b/m4/ax_tls.m4
@@ -0,0 +1,74 @@
+# ===========================================================================
+#          https://www.gnu.org/software/autoconf-archive/ax_tls.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_TLS([action-if-found], [action-if-not-found])
+#
+# DESCRIPTION
+#
+#   Provides a test for the compiler support of thread local storage (TLS)
+#   extensions. Defines TLS if it is found. Currently knows about C++11,
+#   GCC/ICC, and MSVC. I think SunPro uses the same as GCC, and Borland
+#   apparently supports either.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Alan Woodland <ajw05@aber.ac.uk>
+#   Copyright (c) 2010 Diego Elio Petteno` <flameeyes@gmail.com>
+#
+#   This program is free software: you can redistribute it and/or modify it
+#   under the terms of the GNU General Public License as published by the
+#   Free Software Foundation, either version 3 of the License, or (at your
+#   option) any later version.
+#
+#   This program is distributed in the hope that it will be useful, but
+#   WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+#   Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License along
+#   with this program. If not, see <https://www.gnu.org/licenses/>.
+#
+#   As a special exception, the respective Autoconf Macro's copyright owner
+#   gives unlimited permission to copy, distribute and modify the configure
+#   scripts that are the output of Autoconf when processing the Macro. You
+#   need not follow the terms of the GNU General Public License when using
+#   or distributing such scripts, even though portions of the text of the
+#   Macro appear in them. The GNU General Public License (GPL) does govern
+#   all other use of the material that constitutes the Autoconf Macro.
+#
+#   This special exception to the GPL applies to versions of the Autoconf
+#   Macro released by the Autoconf Archive. When you make and distribute a
+#   modified version of the Autoconf Macro, you may extend this special
+#   exception to the GPL to apply to your modified version as well.
+
+#serial 14
+
+AC_DEFUN([AX_TLS], [
+  AC_MSG_CHECKING([for thread local storage (TLS) class])
+  AC_CACHE_VAL([ac_cv_tls],
+   [for ax_tls_keyword in thread_local _Thread_local __thread '__declspec(thread)' none; do
+       AS_CASE([$ax_tls_keyword],
+          [none], [ac_cv_tls=none ; break],
+          [AC_TRY_COMPILE(
+              [#include <stdlib.h>
+               static void
+               foo(void) {
+               static ] $ax_tls_keyword [ int bar;
+               exit(1);
+               }],
+               [],
+               [ac_cv_tls=$ax_tls_keyword ; break],
+               ac_cv_tls=none
+           )])
+    done
+  ])
+  AC_MSG_RESULT([$ac_cv_tls])
+
+  AS_IF([test "$ac_cv_tls" != "none"],
+    [AC_DEFINE_UNQUOTED([TLS],[$ac_cv_tls],[If the compiler supports a TLS storage class define it to that here])
+     m4_ifnblank([$1],[$1])],
+    [m4_ifnblank([$2],[$2])])
+])