Coverage Report

Created: 2023-06-08 06:40

/src/openssl111/crypto/dso/dso_dlfcn.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2000-2019 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_local.h"
20
#include "e_os.h"
21
22
#ifdef DSO_DLFCN
23
24
# ifdef HAVE_DLFCN_H
25
#  ifdef __osf__
26
#   define __EXTENSIONS__
27
#  endif
28
#  include <dlfcn.h>
29
#  define HAVE_DLINFO 1
30
#  if 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
    /* See applicable comments in dso_dl.c */
100
0
    char *filename = DSO_convert_filename(dso, NULL);
101
0
    int flags = DLOPEN_FLAG;
102
0
    int saveerrno = get_last_sys_error();
103
104
0
    if (filename == NULL) {
105
0
        DSOerr(DSO_F_DLFCN_LOAD, DSO_R_NO_FILENAME);
106
0
        goto err;
107
0
    }
108
0
# ifdef RTLD_GLOBAL
109
0
    if (dso->flags & DSO_FLAG_GLOBAL_SYMBOLS)
110
0
        flags |= RTLD_GLOBAL;
111
0
# endif
112
# ifdef _AIX
113
    if (filename[strlen(filename) - 1] == ')')
114
        flags |= RTLD_MEMBER;
115
# endif
116
0
    ptr = dlopen(filename, flags);
117
0
    if (ptr == NULL) {
118
0
        DSOerr(DSO_F_DLFCN_LOAD, DSO_R_LOAD_FAILED);
119
0
        ERR_add_error_data(4, "filename(", filename, "): ", dlerror());
120
0
        goto err;
121
0
    }
122
    /*
123
     * Some dlopen() implementations (e.g. solaris) do no preserve errno, even
124
     * on a successful call.
125
     */
126
0
    set_sys_error(saveerrno);
127
0
    if (!sk_void_push(dso->meth_data, (char *)ptr)) {
128
0
        DSOerr(DSO_F_DLFCN_LOAD, DSO_R_STACK_ERROR);
129
0
        goto err;
130
0
    }
131
    /* Success */
132
0
    dso->loaded_filename = filename;
133
0
    return 1;
134
0
 err:
135
    /* Cleanup! */
136
0
    OPENSSL_free(filename);
137
0
    if (ptr != NULL)
138
0
        dlclose(ptr);
139
0
    return 0;
140
0
}
141
142
static int dlfcn_unload(DSO *dso)
143
0
{
144
0
    void *ptr;
145
0
    if (dso == NULL) {
146
0
        DSOerr(DSO_F_DLFCN_UNLOAD, ERR_R_PASSED_NULL_PARAMETER);
147
0
        return 0;
148
0
    }
149
0
    if (sk_void_num(dso->meth_data) < 1)
150
0
        return 1;
151
0
    ptr = sk_void_pop(dso->meth_data);
152
0
    if (ptr == NULL) {
153
0
        DSOerr(DSO_F_DLFCN_UNLOAD, DSO_R_NULL_HANDLE);
154
        /*
155
         * Should push the value back onto the stack in case of a retry.
156
         */
157
0
        sk_void_push(dso->meth_data, ptr);
158
0
        return 0;
159
0
    }
160
    /* For now I'm not aware of any errors associated with dlclose() */
161
0
    dlclose(ptr);
162
0
    return 1;
163
0
}
164
165
static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname)
166
0
{
167
0
    void *ptr;
168
0
    union {
169
0
        DSO_FUNC_TYPE sym;
170
0
        void *dlret;
171
0
    } u;
172
173
0
    if ((dso == NULL) || (symname == NULL)) {
174
0
        DSOerr(DSO_F_DLFCN_BIND_FUNC, ERR_R_PASSED_NULL_PARAMETER);
175
0
        return NULL;
176
0
    }
177
0
    if (sk_void_num(dso->meth_data) < 1) {
178
0
        DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_STACK_ERROR);
179
0
        return NULL;
180
0
    }
181
0
    ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
182
0
    if (ptr == NULL) {
183
0
        DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_NULL_HANDLE);
184
0
        return NULL;
185
0
    }
186
0
    u.dlret = dlsym(ptr, symname);
187
0
    if (u.dlret == NULL) {
188
0
        DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_SYM_FAILURE);
189
0
        ERR_add_error_data(4, "symname(", symname, "): ", dlerror());
190
0
        return NULL;
191
0
    }
192
0
    return u.sym;
193
0
}
194
195
static char *dlfcn_merger(DSO *dso, const char *filespec1,
196
                          const char *filespec2)
197
0
{
198
0
    char *merged;
199
200
0
    if (!filespec1 && !filespec2) {
201
0
        DSOerr(DSO_F_DLFCN_MERGER, ERR_R_PASSED_NULL_PARAMETER);
202
0
        return NULL;
203
0
    }
204
    /*
205
     * If the first file specification is a rooted path, it rules. same goes
206
     * if the second file specification is missing.
207
     */
208
0
    if (!filespec2 || (filespec1 != NULL && filespec1[0] == '/')) {
209
0
        merged = OPENSSL_strdup(filespec1);
210
0
        if (merged == NULL) {
211
0
            DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
212
0
            return NULL;
213
0
        }
214
0
    }
215
    /*
216
     * If the first file specification is missing, the second one rules.
217
     */
218
0
    else if (!filespec1) {
219
0
        merged = OPENSSL_strdup(filespec2);
220
0
        if (merged == NULL) {
221
0
            DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
222
0
            return NULL;
223
0
        }
224
0
    } else {
225
        /*
226
         * This part isn't as trivial as it looks.  It assumes that the
227
         * second file specification really is a directory, and makes no
228
         * checks whatsoever.  Therefore, the result becomes the
229
         * concatenation of filespec2 followed by a slash followed by
230
         * filespec1.
231
         */
232
0
        int spec2len, len;
233
234
0
        spec2len = strlen(filespec2);
235
0
        len = spec2len + strlen(filespec1);
236
237
0
        if (spec2len && filespec2[spec2len - 1] == '/') {
238
0
            spec2len--;
239
0
            len--;
240
0
        }
241
0
        merged = OPENSSL_malloc(len + 2);
242
0
        if (merged == NULL) {
243
0
            DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
244
0
            return NULL;
245
0
        }
246
0
        strcpy(merged, filespec2);
247
0
        merged[spec2len] = '/';
248
0
        strcpy(&merged[spec2len + 1], filespec1);
249
0
    }
250
0
    return merged;
251
0
}
252
253
static char *dlfcn_name_converter(DSO *dso, const char *filename)
254
0
{
255
0
    char *translated;
256
0
    int len, rsize, transform;
257
258
0
    len = strlen(filename);
259
0
    rsize = len + 1;
260
0
    transform = (strstr(filename, "/") == NULL);
261
0
    if (transform) {
262
        /* We will convert this to "%s.so" or "lib%s.so" etc */
263
0
        rsize += strlen(DSO_EXTENSION);    /* The length of ".so" */
264
0
        if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
265
0
            rsize += 3;         /* The length of "lib" */
266
0
    }
267
0
    translated = OPENSSL_malloc(rsize);
268
0
    if (translated == NULL) {
269
0
        DSOerr(DSO_F_DLFCN_NAME_CONVERTER, DSO_R_NAME_TRANSLATION_FAILED);
270
0
        return NULL;
271
0
    }
272
0
    if (transform) {
273
0
        if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
274
0
            sprintf(translated, "lib%s" DSO_EXTENSION, filename);
275
0
        else
276
0
            sprintf(translated, "%s" DSO_EXTENSION, filename);
277
0
    } else
278
0
        sprintf(translated, "%s", filename);
279
0
    return translated;
280
0
}
281
282
# ifdef __sgi
283
/*-
284
This is a quote from IRIX manual for dladdr(3c):
285
286
     <dlfcn.h> does not contain a prototype for dladdr or definition of
287
     Dl_info.  The #include <dlfcn.h>  in the SYNOPSIS line is traditional,
288
     but contains no dladdr prototype and no IRIX library contains an
289
     implementation.  Write your own declaration based on the code below.
290
291
     The following code is dependent on internal interfaces that are not
292
     part of the IRIX compatibility guarantee; however, there is no future
293
     intention to change this interface, so on a practical level, the code
294
     below is safe to use on IRIX.
295
*/
296
#  include <rld_interface.h>
297
#  ifndef _RLD_INTERFACE_DLFCN_H_DLADDR
298
#   define _RLD_INTERFACE_DLFCN_H_DLADDR
299
typedef struct Dl_info {
300
    const char *dli_fname;
301
    void *dli_fbase;
302
    const char *dli_sname;
303
    void *dli_saddr;
304
    int dli_version;
305
    int dli_reserved1;
306
    long dli_reserved[4];
307
} Dl_info;
308
#  else
309
typedef struct Dl_info Dl_info;
310
#  endif
311
#  define _RLD_DLADDR             14
312
313
static int dladdr(void *address, Dl_info *dl)
314
{
315
    void *v;
316
    v = _rld_new_interface(_RLD_DLADDR, address, dl);
317
    return (int)v;
318
}
319
# endif                         /* __sgi */
320
321
# ifdef _AIX
322
/*-
323
 * See IBM's AIX Version 7.2, Technical Reference:
324
 *  Base Operating System and Extensions, Volume 1 and 2
325
 *  https://www.ibm.com/support/knowledgecenter/ssw_aix_72/com.ibm.aix.base/technicalreferences.htm
326
 */
327
#  include <sys/ldr.h>
328
#  include <errno.h>
329
/* ~ 64 * (sizeof(struct ld_info) + _XOPEN_PATH_MAX + _XOPEN_NAME_MAX) */
330
#  define DLFCN_LDINFO_SIZE 86976
331
typedef struct Dl_info {
332
    const char *dli_fname;
333
} Dl_info;
334
/*
335
 * This dladdr()-implementation will also find the ptrgl (Pointer Glue) virtual
336
 * address of a function, which is just located in the DATA segment instead of
337
 * the TEXT segment.
338
 */
339
static int dladdr(void *ptr, Dl_info *dl)
340
{
341
    uintptr_t addr = (uintptr_t)ptr;
342
    unsigned int found = 0;
343
    struct ld_info *ldinfos, *next_ldi, *this_ldi;
344
345
    if ((ldinfos = OPENSSL_malloc(DLFCN_LDINFO_SIZE)) == NULL) {
346
        errno = ENOMEM;
347
        dl->dli_fname = NULL;
348
        return 0;
349
    }
350
351
    if ((loadquery(L_GETINFO, (void *)ldinfos, DLFCN_LDINFO_SIZE)) < 0) {
352
        /*-
353
         * Error handling is done through errno and dlerror() reading errno:
354
         *  ENOMEM (ldinfos buffer is too small),
355
         *  EINVAL (invalid flags),
356
         *  EFAULT (invalid ldinfos ptr)
357
         */
358
        OPENSSL_free((void *)ldinfos);
359
        dl->dli_fname = NULL;
360
        return 0;
361
    }
362
    next_ldi = ldinfos;
363
364
    do {
365
        this_ldi = next_ldi;
366
        if (((addr >= (uintptr_t)this_ldi->ldinfo_textorg)
367
             && (addr < ((uintptr_t)this_ldi->ldinfo_textorg +
368
                         this_ldi->ldinfo_textsize)))
369
            || ((addr >= (uintptr_t)this_ldi->ldinfo_dataorg)
370
                && (addr < ((uintptr_t)this_ldi->ldinfo_dataorg +
371
                            this_ldi->ldinfo_datasize)))) {
372
            char *buffer, *member;
373
            size_t buffer_sz, member_len;
374
375
            buffer_sz = strlen(this_ldi->ldinfo_filename) + 1;
376
            member = this_ldi->ldinfo_filename + buffer_sz;
377
            if ((member_len = strlen(member)) > 0)
378
                buffer_sz += 1 + member_len + 1;
379
            found = 1;
380
            if ((buffer = OPENSSL_malloc(buffer_sz)) != NULL) {
381
                OPENSSL_strlcpy(buffer, this_ldi->ldinfo_filename, buffer_sz);
382
                if (member_len > 0) {
383
                    /*
384
                     * Need to respect a possible member name and not just
385
                     * returning the path name in this case. See docs:
386
                     * sys/ldr.h, loadquery() and dlopen()/RTLD_MEMBER.
387
                     */
388
                    OPENSSL_strlcat(buffer, "(", buffer_sz);
389
                    OPENSSL_strlcat(buffer, member, buffer_sz);
390
                    OPENSSL_strlcat(buffer, ")", buffer_sz);
391
                }
392
                dl->dli_fname = buffer;
393
            } else {
394
                errno = ENOMEM;
395
            }
396
        } else {
397
            next_ldi = (struct ld_info *)((uintptr_t)this_ldi +
398
                                          this_ldi->ldinfo_next);
399
        }
400
    } while (this_ldi->ldinfo_next && !found);
401
    OPENSSL_free((void *)ldinfos);
402
    return (found && dl->dli_fname != NULL);
403
}
404
# endif                         /* _AIX */
405
406
static int dlfcn_pathbyaddr(void *addr, char *path, int sz)
407
0
{
408
0
# ifdef HAVE_DLINFO
409
0
    Dl_info dli;
410
0
    int len;
411
412
0
    if (addr == NULL) {
413
0
        union {
414
0
            int (*f) (void *, char *, int);
415
0
            void *p;
416
0
        } t = {
417
0
            dlfcn_pathbyaddr
418
0
        };
419
0
        addr = t.p;
420
0
    }
421
422
0
    if (dladdr(addr, &dli)) {
423
0
        len = (int)strlen(dli.dli_fname);
424
0
        if (sz <= 0) {
425
#  ifdef _AIX
426
            OPENSSL_free((void *)dli.dli_fname);
427
#  endif
428
0
            return len + 1;
429
0
        }
430
0
        if (len >= sz)
431
0
            len = sz - 1;
432
0
        memcpy(path, dli.dli_fname, len);
433
0
        path[len++] = 0;
434
#  ifdef _AIX
435
        OPENSSL_free((void *)dli.dli_fname);
436
#  endif
437
0
        return len;
438
0
    }
439
440
0
    ERR_add_error_data(2, "dlfcn_pathbyaddr(): ", dlerror());
441
0
# endif
442
0
    return -1;
443
0
}
444
445
static void *dlfcn_globallookup(const char *name)
446
0
{
447
0
    void *ret = NULL, *handle = dlopen(NULL, RTLD_LAZY);
448
449
0
    if (handle) {
450
0
        ret = dlsym(handle, name);
451
0
        dlclose(handle);
452
0
    }
453
454
0
    return ret;
455
0
}
456
#endif                          /* DSO_DLFCN */