Coverage Report

Created: 2018-08-29 13:53

/src/openssl/crypto/dso/dso_dlfcn.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2000-2018 The OpenSSL Project Authors. All Rights Reserved.
3
 *
4
 * Licensed under the OpenSSL license (the "License").  You may not use
5
 * this file except in compliance with the License.  You can obtain a copy
6
 * in the file LICENSE in the source distribution or at
7
 * https://www.openssl.org/source/license.html
8
 */
9
10
/*
11
 * We need to do this early, because stdio.h includes the header files that
12
 * handle _GNU_SOURCE and other similar macros.  Defining it later is simply
13
 * too late, because those headers are protected from re- inclusion.
14
 */
15
#ifndef _GNU_SOURCE
16
# define _GNU_SOURCE            /* make sure dladdr is declared */
17
#endif
18
19
#include "dso_locl.h"
20
21
#ifdef DSO_DLFCN
22
23
# ifdef HAVE_DLFCN_H
24
#  ifdef __osf__
25
#   define __EXTENSIONS__
26
#  endif
27
#  include <dlfcn.h>
28
#  define HAVE_DLINFO 1
29
#  if defined(__CYGWIN__) || \
30
     defined(__SCO_VERSION__) || defined(_SCO_ELF) || \
31
     (defined(__osf__) && !defined(RTLD_NEXT))     || \
32
     (defined(__OpenBSD__) && !defined(RTLD_SELF)) || \
33
        defined(__ANDROID__)
34
#   undef HAVE_DLINFO
35
#  endif
36
# endif
37
38
/* Part of the hack in "dlfcn_load" ... */
39
# define DSO_MAX_TRANSLATED_SIZE 256
40
41
static int dlfcn_load(DSO *dso);
42
static int dlfcn_unload(DSO *dso);
43
static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname);
44
static char *dlfcn_name_converter(DSO *dso, const char *filename);
45
static char *dlfcn_merger(DSO *dso, const char *filespec1,
46
                          const char *filespec2);
47
static int dlfcn_pathbyaddr(void *addr, char *path, int sz);
48
static void *dlfcn_globallookup(const char *name);
49
50
static DSO_METHOD dso_meth_dlfcn = {
51
    "OpenSSL 'dlfcn' shared library method",
52
    dlfcn_load,
53
    dlfcn_unload,
54
    dlfcn_bind_func,
55
    NULL,                       /* ctrl */
56
    dlfcn_name_converter,
57
    dlfcn_merger,
58
    NULL,                       /* init */
59
    NULL,                       /* finish */
60
    dlfcn_pathbyaddr,
61
    dlfcn_globallookup
62
};
63
64
DSO_METHOD *DSO_METHOD_openssl(void)
65
0
{
66
0
    return &dso_meth_dlfcn;
67
0
}
68
69
/*
70
 * Prior to using the dlopen() function, we should decide on the flag we
71
 * send. There's a few different ways of doing this and it's a messy
72
 * venn-diagram to match up which platforms support what. So as we don't have
73
 * autoconf yet, I'm implementing a hack that could be hacked further
74
 * relatively easily to deal with cases as we find them. Initially this is to
75
 * cope with OpenBSD.
76
 */
77
# if defined(__OpenBSD__) || defined(__NetBSD__)
78
#  ifdef DL_LAZY
79
#   define DLOPEN_FLAG DL_LAZY
80
#  else
81
#   ifdef RTLD_NOW
82
#    define DLOPEN_FLAG RTLD_NOW
83
#   else
84
#    define DLOPEN_FLAG 0
85
#   endif
86
#  endif
87
# else
88
0
#  define DLOPEN_FLAG RTLD_NOW  /* Hope this works everywhere else */
89
# endif
90
91
/*
92
 * For this DSO_METHOD, our meth_data STACK will contain; (i) the handle
93
 * (void*) returned from dlopen().
94
 */
95
96
static int dlfcn_load(DSO *dso)
97
0
{
98
0
    void *ptr = NULL;
99
0
    /* See applicable comments in dso_dl.c */
100
0
    char *filename = DSO_convert_filename(dso, NULL);
101
0
    int flags = DLOPEN_FLAG;
102
0
103
0
    if (filename == NULL) {
104
0
        DSOerr(DSO_F_DLFCN_LOAD, DSO_R_NO_FILENAME);
105
0
        goto err;
106
0
    }
107
0
# ifdef RTLD_GLOBAL
108
0
    if (dso->flags & DSO_FLAG_GLOBAL_SYMBOLS)
109
0
        flags |= RTLD_GLOBAL;
110
0
# endif
111
# ifdef _AIX
112
    if (filename[strlen(filename) - 1] == ')')
113
        flags |= RTLD_MEMBER;
114
# endif
115
    ptr = dlopen(filename, flags);
116
0
    if (ptr == NULL) {
117
0
        DSOerr(DSO_F_DLFCN_LOAD, DSO_R_LOAD_FAILED);
118
0
        ERR_add_error_data(4, "filename(", filename, "): ", dlerror());
119
0
        goto err;
120
0
    }
121
0
    if (!sk_void_push(dso->meth_data, (char *)ptr)) {
122
0
        DSOerr(DSO_F_DLFCN_LOAD, DSO_R_STACK_ERROR);
123
0
        goto err;
124
0
    }
125
0
    /* Success */
126
0
    dso->loaded_filename = filename;
127
0
    return 1;
128
0
 err:
129
0
    /* Cleanup! */
130
0
    OPENSSL_free(filename);
131
0
    if (ptr != NULL)
132
0
        dlclose(ptr);
133
0
    return 0;
134
0
}
135
136
static int dlfcn_unload(DSO *dso)
137
0
{
138
0
    void *ptr;
139
0
    if (dso == NULL) {
140
0
        DSOerr(DSO_F_DLFCN_UNLOAD, ERR_R_PASSED_NULL_PARAMETER);
141
0
        return 0;
142
0
    }
143
0
    if (sk_void_num(dso->meth_data) < 1)
144
0
        return 1;
145
0
    ptr = sk_void_pop(dso->meth_data);
146
0
    if (ptr == NULL) {
147
0
        DSOerr(DSO_F_DLFCN_UNLOAD, DSO_R_NULL_HANDLE);
148
0
        /*
149
0
         * Should push the value back onto the stack in case of a retry.
150
0
         */
151
0
        sk_void_push(dso->meth_data, ptr);
152
0
        return 0;
153
0
    }
154
0
    /* For now I'm not aware of any errors associated with dlclose() */
155
0
    dlclose(ptr);
156
0
    return 1;
157
0
}
158
159
static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname)
160
0
{
161
0
    void *ptr;
162
0
    union {
163
0
        DSO_FUNC_TYPE sym;
164
0
        void *dlret;
165
0
    } u;
166
0
167
0
    if ((dso == NULL) || (symname == NULL)) {
168
0
        DSOerr(DSO_F_DLFCN_BIND_FUNC, ERR_R_PASSED_NULL_PARAMETER);
169
0
        return NULL;
170
0
    }
171
0
    if (sk_void_num(dso->meth_data) < 1) {
172
0
        DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_STACK_ERROR);
173
0
        return NULL;
174
0
    }
175
0
    ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
176
0
    if (ptr == NULL) {
177
0
        DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_NULL_HANDLE);
178
0
        return NULL;
179
0
    }
180
0
    u.dlret = dlsym(ptr, symname);
181
0
    if (u.dlret == NULL) {
182
0
        DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_SYM_FAILURE);
183
0
        ERR_add_error_data(4, "symname(", symname, "): ", dlerror());
184
0
        return NULL;
185
0
    }
186
0
    return u.sym;
187
0
}
188
189
static char *dlfcn_merger(DSO *dso, const char *filespec1,
190
                          const char *filespec2)
191
0
{
192
0
    char *merged;
193
0
194
0
    if (!filespec1 && !filespec2) {
195
0
        DSOerr(DSO_F_DLFCN_MERGER, ERR_R_PASSED_NULL_PARAMETER);
196
0
        return NULL;
197
0
    }
198
0
    /*
199
0
     * If the first file specification is a rooted path, it rules. same goes
200
0
     * if the second file specification is missing.
201
0
     */
202
0
    if (!filespec2 || (filespec1 != NULL && filespec1[0] == '/')) {
203
0
        merged = OPENSSL_strdup(filespec1);
204
0
        if (merged == NULL) {
205
0
            DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
206
0
            return NULL;
207
0
        }
208
0
    }
209
0
    /*
210
0
     * If the first file specification is missing, the second one rules.
211
0
     */
212
0
    else if (!filespec1) {
213
0
        merged = OPENSSL_strdup(filespec2);
214
0
        if (merged == NULL) {
215
0
            DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
216
0
            return NULL;
217
0
        }
218
0
    } else {
219
0
        /*
220
0
         * This part isn't as trivial as it looks.  It assumes that the
221
0
         * second file specification really is a directory, and makes no
222
0
         * checks whatsoever.  Therefore, the result becomes the
223
0
         * concatenation of filespec2 followed by a slash followed by
224
0
         * filespec1.
225
0
         */
226
0
        int spec2len, len;
227
0
228
0
        spec2len = strlen(filespec2);
229
0
        len = spec2len + strlen(filespec1);
230
0
231
0
        if (spec2len && filespec2[spec2len - 1] == '/') {
232
0
            spec2len--;
233
0
            len--;
234
0
        }
235
0
        merged = OPENSSL_malloc(len + 2);
236
0
        if (merged == NULL) {
237
0
            DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
238
0
            return NULL;
239
0
        }
240
0
        strcpy(merged, filespec2);
241
0
        merged[spec2len] = '/';
242
0
        strcpy(&merged[spec2len + 1], filespec1);
243
0
    }
244
0
    return merged;
245
0
}
246
247
static char *dlfcn_name_converter(DSO *dso, const char *filename)
248
0
{
249
0
    char *translated;
250
0
    int len, rsize, transform;
251
0
252
0
    len = strlen(filename);
253
0
    rsize = len + 1;
254
0
    transform = (strstr(filename, "/") == NULL);
255
0
    if (transform) {
256
0
        /* We will convert this to "%s.so" or "lib%s.so" etc */
257
0
        rsize += strlen(DSO_EXTENSION);    /* The length of ".so" */
258
0
        if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
259
0
            rsize += 3;         /* The length of "lib" */
260
0
    }
261
0
    translated = OPENSSL_malloc(rsize);
262
0
    if (translated == NULL) {
263
0
        DSOerr(DSO_F_DLFCN_NAME_CONVERTER, DSO_R_NAME_TRANSLATION_FAILED);
264
0
        return NULL;
265
0
    }
266
0
    if (transform) {
267
0
        if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
268
0
            sprintf(translated, "lib%s" DSO_EXTENSION, filename);
269
0
        else
270
0
            sprintf(translated, "%s" DSO_EXTENSION, filename);
271
0
    } else
272
0
        sprintf(translated, "%s", filename);
273
0
    return translated;
274
0
}
275
276
# ifdef __sgi
277
/*-
278
This is a quote from IRIX manual for dladdr(3c):
279
280
     <dlfcn.h> does not contain a prototype for dladdr or definition of
281
     Dl_info.  The #include <dlfcn.h>  in the SYNOPSIS line is traditional,
282
     but contains no dladdr prototype and no IRIX library contains an
283
     implementation.  Write your own declaration based on the code below.
284
285
     The following code is dependent on internal interfaces that are not
286
     part of the IRIX compatibility guarantee; however, there is no future
287
     intention to change this interface, so on a practical level, the code
288
     below is safe to use on IRIX.
289
*/
290
#  include <rld_interface.h>
291
#  ifndef _RLD_INTERFACE_DLFCN_H_DLADDR
292
#   define _RLD_INTERFACE_DLFCN_H_DLADDR
293
typedef struct Dl_info {
294
    const char *dli_fname;
295
    void *dli_fbase;
296
    const char *dli_sname;
297
    void *dli_saddr;
298
    int dli_version;
299
    int dli_reserved1;
300
    long dli_reserved[4];
301
} Dl_info;
302
#  else
303
typedef struct Dl_info Dl_info;
304
#  endif
305
#  define _RLD_DLADDR             14
306
307
static int dladdr(void *address, Dl_info *dl)
308
{
309
    void *v;
310
    v = _rld_new_interface(_RLD_DLADDR, address, dl);
311
    return (int)v;
312
}
313
# endif                         /* __sgi */
314
315
# ifdef _AIX
316
/*-
317
 * See IBM's AIX Version 7.2, Technical Reference:
318
 *  Base Operating System and Extensions, Volume 1 and 2
319
 *  https://www.ibm.com/support/knowledgecenter/ssw_aix_72/com.ibm.aix.base/technicalreferences.htm
320
 */
321
#  include <sys/ldr.h>
322
#  include <errno.h>
323
/* ~ 64 * (sizeof(struct ld_info) + _XOPEN_PATH_MAX + _XOPEN_NAME_MAX) */
324
#  define DLFCN_LDINFO_SIZE 86976
325
typedef struct Dl_info {
326
    const char *dli_fname;
327
} Dl_info;
328
/*
329
 * This dladdr()-implementation will also find the ptrgl (Pointer Glue) virtual
330
 * address of a function, which is just located in the DATA segment instead of
331
 * the TEXT segment.
332
 */
333
static int dladdr(void *ptr, Dl_info *dl)
334
{
335
    uintptr_t addr = (uintptr_t)ptr;
336
    unsigned int found = 0;
337
    struct ld_info *ldinfos, *next_ldi, *this_ldi;
338
339
    if ((ldinfos = OPENSSL_malloc(DLFCN_LDINFO_SIZE)) == NULL) {
340
        errno = ENOMEM;
341
        dl->dli_fname = NULL;
342
        return 0;
343
    }
344
345
    if ((loadquery(L_GETINFO, (void *)ldinfos, DLFCN_LDINFO_SIZE)) < 0) {
346
        /*-
347
         * Error handling is done through errno and dlerror() reading errno:
348
         *  ENOMEM (ldinfos buffer is too small),
349
         *  EINVAL (invalid flags),
350
         *  EFAULT (invalid ldinfos ptr)
351
         */
352
        OPENSSL_free((void *)ldinfos);
353
        dl->dli_fname = NULL;
354
        return 0;
355
    }
356
    next_ldi = ldinfos;
357
358
    do {
359
        this_ldi = next_ldi;
360
        if (((addr >= (uintptr_t)this_ldi->ldinfo_textorg)
361
             && (addr < ((uintptr_t)this_ldi->ldinfo_textorg +
362
                         this_ldi->ldinfo_textsize)))
363
            || ((addr >= (uintptr_t)this_ldi->ldinfo_dataorg)
364
                && (addr < ((uintptr_t)this_ldi->ldinfo_dataorg +
365
                            this_ldi->ldinfo_datasize)))) {
366
            char *buffer, *member;
367
            size_t buffer_sz, member_len;
368
369
            buffer_sz = strlen(this_ldi->ldinfo_filename) + 1;
370
            member = this_ldi->ldinfo_filename + buffer_sz;
371
            if ((member_len = strlen(member)) > 0)
372
                buffer_sz += 1 + member_len + 1;
373
            found = 1;
374
            if ((buffer = OPENSSL_malloc(buffer_sz)) != NULL) {
375
                OPENSSL_strlcpy(buffer, this_ldi->ldinfo_filename, buffer_sz);
376
                if (member_len > 0) {
377
                    /*
378
                     * Need to respect a possible member name and not just
379
                     * returning the path name in this case. See docs:
380
                     * sys/ldr.h, loadquery() and dlopen()/RTLD_MEMBER.
381
                     */
382
                    OPENSSL_strlcat(buffer, "(", buffer_sz);
383
                    OPENSSL_strlcat(buffer, member, buffer_sz);
384
                    OPENSSL_strlcat(buffer, ")", buffer_sz);
385
                }
386
                dl->dli_fname = buffer;
387
            } else {
388
                errno = ENOMEM;
389
            }
390
        } else {
391
            next_ldi = (struct ld_info *)((uintptr_t)this_ldi +
392
                                          this_ldi->ldinfo_next);
393
        }
394
    } while (this_ldi->ldinfo_next && !found);
395
    OPENSSL_free((void *)ldinfos);
396
    return (found && dl->dli_fname != NULL);
397
}
398
# endif                         /* _AIX */
399
400
static int dlfcn_pathbyaddr(void *addr, char *path, int sz)
401
0
{
402
0
# ifdef HAVE_DLINFO
403
0
    Dl_info dli;
404
0
    int len;
405
0
406
0
    if (addr == NULL) {
407
0
        union {
408
0
            int (*f) (void *, char *, int);
409
0
            void *p;
410
0
        } t = {
411
0
            dlfcn_pathbyaddr
412
0
        };
413
0
        addr = t.p;
414
0
    }
415
0
416
0
    if (dladdr(addr, &dli)) {
417
0
        len = (int)strlen(dli.dli_fname);
418
0
        if (sz <= 0) {
419
#  ifdef _AIX
420
            OPENSSL_free((void *)dli.dli_fname);
421
#  endif
422
            return len + 1;
423
0
        }
424
0
        if (len >= sz)
425
0
            len = sz - 1;
426
0
        memcpy(path, dli.dli_fname, len);
427
0
        path[len++] = 0;
428
#  ifdef _AIX
429
        OPENSSL_free((void *)dli.dli_fname);
430
#  endif
431
        return len;
432
0
    }
433
0
434
0
    ERR_add_error_data(2, "dlfcn_pathbyaddr(): ", dlerror());
435
0
# endif
436
0
    return -1;
437
0
}
438
439
static void *dlfcn_globallookup(const char *name)
440
0
{
441
0
    void *ret = NULL, *handle = dlopen(NULL, RTLD_LAZY);
442
0
443
0
    if (handle) {
444
0
        ret = dlsym(handle, name);
445
0
        dlclose(handle);
446
0
    }
447
0
448
0
    return ret;
449
0
}
450
#endif                          /* DSO_DLFCN */