shunloadtest.c 4.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/*
 * Copyright (C) 2011 Red Hat, Inc.
 *
 * This library 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 library 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
15
 * License along with this library.  If not, see
O
Osier Yang 已提交
16
 * <http://www.gnu.org/licenses/>.
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
 *
 */

/*
 * When libvirt initializes, it creates a thread local for storing
 * the last virErrorPtr instance. It also registers a cleanup
 * callback for the thread local that will be invoked whenever
 * a thread exits.
 *
 * If the libvirt.so library was dlopen()'d and is dlclose()'d
 * while there is still a thread present, then when that thread
 * later exits, the libvirt cleanup callback will be invoked.
 * Unfortunately libvirt.so will no longer be in memory so the
 * callback SEGVs (if you're lucky), or invokes unlreated
 * code at the same address as the old callback (if you're
 * unlucky).
 *
 * To fix the problem libvirt is linked '-z nodelete' which
 * prevents the code being removed from memory at dlclose().
 *
 * This test case demonstrates this SEGV scenario. If this
 * test does not SEGV, then the '-z nodelete' fix is working
 */

#include <config.h>

43 44
#include "testutils.h"

45 46 47 48 49 50 51 52 53 54 55 56 57 58
#ifdef linux

# include <dlfcn.h>
# include <pthread.h>
# include <stdbool.h>
# include <stdio.h>
# include <unistd.h>
# include <signal.h>

# include "internal.h"

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
bool running = false;
59
bool failstart = false;
60 61 62 63
bool quit = false;

static void *threadMain(void *arg)
{
64 65 66 67 68 69 70 71 72 73 74
    int (*startup)(void) = arg;

    if (startup() < 0) {
        pthread_mutex_lock(&lock);
        failstart = true;
        pthread_cond_signal(&cond);
    } else {
        pthread_mutex_lock(&lock);
        running = true;
        pthread_cond_signal(&cond);
    }
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

    while (!quit) {
        pthread_cond_wait(&cond, &lock);
    }
    pthread_mutex_unlock(&lock);

    return NULL;
}

static void sigHandler(int sig)
{
    ignore_value(write(STDERR_FILENO, "FAIL\n", 5));
    signal(sig, SIG_DFL);
    raise(sig);
}

/* We're not using the testutils.c main() wrapper because
 * we don't want  'shunloadtest' itself to link against
 * libvirt.so. We need to test dlopen()'ing of libvirt.so
 */
int main(int argc ATTRIBUTE_UNUSED, char **argv)
{
    void (*startup)(void);
    pthread_t t;
    void *lib;
    char *theprogname;

    theprogname = argv[0];
    if (STRPREFIX(theprogname, "./"))
        theprogname += 2;

    fprintf(stderr, "TEST: %s\n", theprogname);
    fprintf(stderr, "      .%*s 1   ", 39, "");
    signal(SIGSEGV, sigHandler);

D
Daniel P. Berrange 已提交
110
    if (!(lib = dlopen("./.libs/libshunload.so", RTLD_LAZY))) {
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
        fprintf(stderr, "Cannot load ./.libs/libshunload.so %s\n", dlerror());
        return 1;
    }
    if (!(startup = dlsym(lib, "shunloadStart"))) {
        fprintf(stderr, "Cannot find shunloadStart %s\n", dlerror());
        return 1;
    }

    /*
     * Create a thread which is going to initialize libvirt
     * and raise an error
     */
    pthread_create(&t, NULL, threadMain, startup);

    /* Wait for the thread to start and call libvirt */
    pthread_mutex_lock(&lock);
127
    while (!running && !failstart) {
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
        pthread_cond_wait(&cond, &lock);
    }

    /* Close the shared library (and thus make libvirt.so
     * non-resident */
    dlclose(lib);

    /* Tell the thread to quit */
    quit = true;
    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&lock);

    pthread_join(t, NULL);

    /* If we got to here the thread successfully exited without
     * causing a SEGV !
     */

146 147 148 149
    if (failstart)
        fprintf(stderr, "FAIL to initialize libvirt\n");
    else
        fprintf(stderr, "OK\n");
150 151 152 153 154 155 156 157 158 159 160 161

    return 0;
}

#else

int main(void)
{
    return EXIT_AM_SKIP;
}

#endif