override.c 7.87 KB
Newer Older
1
/*****************************************************************************
2
 * override.c: overridden function calls for VLC media player
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 *****************************************************************************
 * Copyright (C) 2010 Rémi Denis-Courmont
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <stdbool.h>
26
#define MAX_ERRORS 5
27 28 29

void vlc_enable_override (void);

30
#if defined (__GNUC__) \
31 32 33 34 35 36
 && (defined (__ELF__) && !defined (__sun__))
/* Solaris crashes on printf("%s", NULL); which is legal, but annoying. */

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
37
#include <string.h>
38
#include <dlfcn.h>
39
#include <pthread.h>
40 41
#ifdef HAVE_EXECINFO_H
# include <execinfo.h>
42
# include <unistd.h>
43
#endif
44 45 46
#ifdef NDEBUG
# undef HAVE_BACKTRACE
#endif
47

48 49 50 51 52 53 54 55 56 57 58 59 60
static bool override = false;

static void vlc_reset_override (void)
{
    override = false;
}

void vlc_enable_override (void)
{
    override = true;
    pthread_atfork (NULL, NULL, vlc_reset_override);
}

61 62
static void vlogbug (unsigned *pc, const char *level, const char *func,
                     const char *fmt, va_list ap)
63
{
64
#ifdef HAVE_BACKTRACE
65
    const size_t framec = 5;
66 67 68 69
    void *framev[framec];

    backtrace (framev, framec);
#endif
70
    flockfile (stderr);
71 72 73 74 75 76 77
    if (*pc < MAX_ERRORS)
    {
        (*pc)++;
        fprintf (stderr, "%s: call to %s(", level, func);
        vfprintf (stderr, fmt, ap);
        fputs (")\n", stderr);
        fflush (stderr);
78
#ifdef HAVE_BACKTRACE
79
        backtrace_symbols_fd (framev + 2, framec - 2, STDERR_FILENO);
80
#endif
81
    }
82 83 84
    funlockfile (stderr);
}

85 86
static void logbug (unsigned *pc, const char *level, const char *func,
                    const char *fmt, ...)
87 88 89 90
{
    va_list ap;

    va_start (ap, fmt);
91
    vlogbug (pc, level, func, fmt, ap);
92 93 94 95 96 97 98 99
    va_end (ap);
}

static void *getsym (const char *name)
{
    void *sym = dlsym (RTLD_NEXT, name);
    if (sym == NULL)
    {
100 101
        fprintf (stderr, "Cannot resolve symbol %s: %s\n", name,
                 dlerror ());
102 103 104 105 106
        abort ();
    }
    return sym;
}

107 108 109 110 111 112
#define LOG(level, ...) \
    do { \
        static unsigned counter = 0; \
        logbug(&counter, level, __func__, __VA_ARGS__); \
    } while (0)

113 114
/* Evil non-standard GNU C macro ;)
 *  typeof keyword,
115
 *  statement-expression
116
 */
117
#define CALL(func, ...) \
118
({ typeof (func) *sym = getsym ( # func); sym (__VA_ARGS__); })
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
/*** Environment ***
 *
 * "Conforming multi-threaded applications shall not use the environ variable
 *  to access or modify any environment variable while any other thread is
 *  concurrently modifying any environment variable." -- POSIX.
 *
 * Some evil libraries modify the environment. We currently ignore the calls as
 * they could crash the process. This may cause funny behaviour though. */
int putenv (char *str)
{
    if (override)
    {
        LOG("Blocked", "\"%s\"", str);
        return 0;
    }
    return CALL(putenv, str);
}

int setenv (const char *name, const char *value, int overwrite)
{
    if (override)
    {
        LOG("Blocked", "\"%s\", \"%s\", %d", name, value, overwrite);
        return 0;
    }
    return CALL(setenv, name, value, overwrite);
}

int unsetenv (const char *name)
{
    if (override)
    {
        LOG("Blocked", "\"%s\"", name);
        return 0;
    }
    return CALL(unsetenv, name);
}


159 160 161 162 163 164 165 166
/*** Pseudo random numbers ***
 *
 * The C PRNG is not thread-safe (and generally sucks, the POSIX 48-bits PRNG
 * is much better as a reproducible non-secure PRNG). To work around this, we
 * force evil callers to serialize. This makes the call safe, but fails to
 * preserve reproducibility of the number sequence (which usually does not
 * matter).
 **/
167 168 169 170 171
static struct
{
    pthread_mutex_t lock;
    unsigned int seed;
} prng = { PTHREAD_MUTEX_INITIALIZER, 0, };
172 173 174

void srand (unsigned int seed)
{
175
    pthread_mutex_lock (&prng.lock);
176
    LOG("Warning", "%u", seed);
177 178
    prng.seed = seed;
    pthread_mutex_unlock (&prng.lock);
179 180 181 182 183 184
}

int rand (void)
{
    int ret;

185
    pthread_mutex_lock (&prng.lock);
186
    LOG("Warning", "");
187 188
    ret = rand_r (&prng.seed);
    pthread_mutex_unlock (&prng.lock);
189 190 191 192
    return ret;
}


193
#if 0
194 195 196
/** Signals **/
#include <signal.h>

197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
static bool blocked_signal (int num)
{
    switch (num)
    {
        case SIGINT:
        case SIGHUP:
        case SIGQUIT:
        case SIGTERM:
        case SIGPIPE:
        case SIGCHLD:
            return true;
    }
    return false;
}

212 213 214 215
void (*signal (int signum, void (*handler) (int))) (int)
{
    if (override)
    {
216 217 218 219 220
        if (handler != SIG_IGN && handler != SIG_DFL)
            goto error;
        if (!blocked_signal (signum))
            goto error;
        /* For our blocked signals, the handler won't matter much... */
221 222
        if (handler == SIG_DFL)
            LOG("Warning", "%d, SIG_DFL", signum, handler);
223 224
    }
    return CALL(signal, signum, handler);
225 226 227
error:
    LOG("Blocked", "%d, %p", signum, handler);
    return SIG_DFL;
228 229 230 231
}

int sigaction (int signum, const struct sigaction *act, struct sigaction *old)
{
232 233 234 235 236
    if (override && act != NULL)
    {
        if ((act->sa_flags & SA_SIGINFO)
         || (act->sa_handler != SIG_IGN && act->sa_handler != SIG_DFL))
            goto error;
237 238
        if (act->sa_handler == SIG_DFL)
            LOG("Warning", "%d, %p, SIG_DFL", signum, act);
239
    }
240
    return CALL(sigaction, signum, act, old);
241 242 243
error:
    LOG("Blocked", "%d, %p, %p", signum, act, old);
    return -1;
244
}
245
#endif
246 247


248 249
/*** Locales ***
 * setlocale() is not thread-safe and has a tendency to crash other threads as
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
 * quite many libc and libintl calls depend on the locale.
 * Use uselocale() instead for thread-safety.
 */
#include <locale.h>

char *setlocale (int cat, const char *locale)
{
    if (override && locale != NULL)
    {
        LOG("Blocked", "%d, \"%s\"", cat, locale);
        return NULL;
    }
    return CALL(setlocale, cat, locale);
}


266 267 268 269 270 271 272 273 274 275 276 277 278 279
/* strerror() is not thread-safe in theory (POSIX), nor in practice (glibc).
 * This caused quite nasty crashes in the history of VLC/Linux. */
char *strerror (int val)
{
    if (override)
    {
        static const char msg[] =
            "Error message unavailable (use strerror_r instead of strerror)!";
        LOG("Blocked", "%d", val);
        return (char *)msg;
    }
    return CALL(strerror, val);
}

280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
/*** Xlib ****/
#ifdef HAVE_X11_XLIB_H
# include <X11/Xlib.h>

static pthread_mutex_t xlib_lock = PTHREAD_MUTEX_INITIALIZER;

int (*XSetErrorHandler (int (*handler) (Display *, XErrorEvent *)))
     (Display *, XErrorEvent *)
{
    if (override)
    {
        int (*ret) (Display *, XErrorEvent *);

        pthread_mutex_lock (&xlib_lock);
        LOG("Error", "%p", handler);
        ret = CALL(XSetErrorHandler, handler);
        pthread_mutex_unlock (&xlib_lock);
        return ret;
    }
    return CALL(XSetErrorHandler, handler);
}

int (*XSetIOErrorHandler (int (*handler) (Display *))) (Display *)
{
    if (override)
    {
        int (*ret) (Display *);

        pthread_mutex_lock (&xlib_lock);
        LOG("Error", "%p", handler);
        ret = CALL(XSetIOErrorHandler, handler);
        pthread_mutex_unlock (&xlib_lock);
        return ret;
    }
    return CALL(XSetIOErrorHandler, handler);
}
#endif
317
#else
318
void vlc_enable_override (void)
319 320 321
{
}
#endif