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])])
+])
|