Coverage Report

Created: 2025-12-31 06:58

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