check if pointer is != NULL before dereferencing it (Coverity CID 40)
[openssl.git] / crypto / dso / dso_dlfcn.c
1 /* dso_dlfcn.c -*- mode:C; c-file-style: "eay" -*- */
2 /* Written by Geoff Thorpe (geoff@geoffthorpe.net) for the OpenSSL
3  * project 2000.
4  */
5 /* ====================================================================
6  * Copyright (c) 2000 The OpenSSL Project.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer. 
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in
17  *    the documentation and/or other materials provided with the
18  *    distribution.
19  *
20  * 3. All advertising materials mentioning features or use of this
21  *    software must display the following acknowledgment:
22  *    "This product includes software developed by the OpenSSL Project
23  *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
24  *
25  * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
26  *    endorse or promote products derived from this software without
27  *    prior written permission. For written permission, please contact
28  *    licensing@OpenSSL.org.
29  *
30  * 5. Products derived from this software may not be called "OpenSSL"
31  *    nor may "OpenSSL" appear in their names without prior written
32  *    permission of the OpenSSL Project.
33  *
34  * 6. Redistributions of any form whatsoever must retain the following
35  *    acknowledgment:
36  *    "This product includes software developed by the OpenSSL Project
37  *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
38  *
39  * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
40  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
41  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
42  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
43  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
45  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
46  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
48  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
49  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
50  * OF THE POSSIBILITY OF SUCH DAMAGE.
51  * ====================================================================
52  *
53  * This product includes cryptographic software written by Eric Young
54  * (eay@cryptsoft.com).  This product includes software written by Tim
55  * Hudson (tjh@cryptsoft.com).
56  *
57  */
58
59 /* We need to do this early, because stdio.h includes the header files
60    that handle _GNU_SOURCE and other similar macros.  Defining it later
61    is simply too late, because those headers are protected from re-
62    inclusion.  */
63 #ifdef __linux
64 # ifndef _GNU_SOURCE
65 #  define _GNU_SOURCE   /* make sure dladdr is declared */
66 # endif
67 # define HAVE_DLINFO 1
68 #endif
69
70 #include <stdio.h>
71 #include "cryptlib.h"
72 #include <openssl/dso.h>
73
74 #ifndef DSO_DLFCN
75 DSO_METHOD *DSO_METHOD_dlfcn(void)
76         {
77         return NULL;
78         }
79 #else
80
81 #ifdef HAVE_DLFCN_H
82
83 #include <dlfcn.h>
84 #endif
85
86 /* Part of the hack in "dlfcn_load" ... */
87 #define DSO_MAX_TRANSLATED_SIZE 256
88
89 static int dlfcn_load(DSO *dso);
90 static int dlfcn_unload(DSO *dso);
91 static void *dlfcn_bind_var(DSO *dso, const char *symname);
92 static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname);
93 #if 0
94 static int dlfcn_unbind(DSO *dso, char *symname, void *symptr);
95 static int dlfcn_init(DSO *dso);
96 static int dlfcn_finish(DSO *dso);
97 static long dlfcn_ctrl(DSO *dso, int cmd, long larg, void *parg);
98 #endif
99 static char *dlfcn_name_converter(DSO *dso, const char *filename);
100 static char *dlfcn_merger(DSO *dso, const char *filespec1,
101         const char *filespec2);
102 static int dlfcn_pathbyaddr(void *addr,char *path,int sz);
103 static void *dlfcn_globallookup(const char *name);
104
105 static DSO_METHOD dso_meth_dlfcn = {
106         "OpenSSL 'dlfcn' shared library method",
107         dlfcn_load,
108         dlfcn_unload,
109         dlfcn_bind_var,
110         dlfcn_bind_func,
111 /* For now, "unbind" doesn't exist */
112 #if 0
113         NULL, /* unbind_var */
114         NULL, /* unbind_func */
115 #endif
116         NULL, /* ctrl */
117         dlfcn_name_converter,
118         dlfcn_merger,
119         NULL, /* init */
120         NULL, /* finish */
121         dlfcn_pathbyaddr,
122         dlfcn_globallookup
123         };
124
125 DSO_METHOD *DSO_METHOD_dlfcn(void)
126         {
127         return(&dso_meth_dlfcn);
128         }
129
130 /* Prior to using the dlopen() function, we should decide on the flag
131  * we send. There's a few different ways of doing this and it's a
132  * messy venn-diagram to match up which platforms support what. So
133  * as we don't have autoconf yet, I'm implementing a hack that could
134  * be hacked further relatively easily to deal with cases as we find
135  * them. Initially this is to cope with OpenBSD. */
136 #if defined(__OpenBSD__) || defined(__NetBSD__)
137 #       define HAVE_DLINFO 1
138 #       ifdef DL_LAZY
139 #               define DLOPEN_FLAG DL_LAZY
140 #       else
141 #               ifdef RTLD_NOW
142 #                       define DLOPEN_FLAG RTLD_NOW
143 #               else
144 #                       define DLOPEN_FLAG 0
145 #               endif
146 #       endif
147 #else
148 #       ifdef OPENSSL_SYS_SUNOS
149 #               define HAVE_DLINFO 1
150 #               define DLOPEN_FLAG 1
151 #       else
152 #               define DLOPEN_FLAG RTLD_NOW /* Hope this works everywhere else */
153 #       endif
154 #endif
155
156 /* For this DSO_METHOD, our meth_data STACK will contain;
157  * (i) the handle (void*) returned from dlopen().
158  */
159
160 static int dlfcn_load(DSO *dso)
161         {
162         void *ptr = NULL;
163         /* See applicable comments in dso_dl.c */
164         char *filename = DSO_convert_filename(dso, NULL);
165         int flags = DLOPEN_FLAG;
166
167         if(filename == NULL)
168                 {
169                 DSOerr(DSO_F_DLFCN_LOAD,DSO_R_NO_FILENAME);
170                 goto err;
171                 }
172
173 #ifdef RTLD_GLOBAL
174         if (dso->flags & DSO_FLAG_GLOBAL_SYMBOLS)
175                 flags |= RTLD_GLOBAL;
176 #endif
177         ptr = dlopen(filename, flags);
178         if(ptr == NULL)
179                 {
180                 DSOerr(DSO_F_DLFCN_LOAD,DSO_R_LOAD_FAILED);
181                 ERR_add_error_data(4, "filename(", filename, "): ", dlerror());
182                 goto err;
183                 }
184         if(!sk_push(dso->meth_data, (char *)ptr))
185                 {
186                 DSOerr(DSO_F_DLFCN_LOAD,DSO_R_STACK_ERROR);
187                 goto err;
188                 }
189         /* Success */
190         dso->loaded_filename = filename;
191         return(1);
192 err:
193         /* Cleanup! */
194         if(filename != NULL)
195                 OPENSSL_free(filename);
196         if(ptr != NULL)
197                 dlclose(ptr);
198         return(0);
199 }
200
201 static int dlfcn_unload(DSO *dso)
202         {
203         void *ptr;
204         if(dso == NULL)
205                 {
206                 DSOerr(DSO_F_DLFCN_UNLOAD,ERR_R_PASSED_NULL_PARAMETER);
207                 return(0);
208                 }
209         if(sk_num(dso->meth_data) < 1)
210                 return(1);
211         ptr = (void *)sk_pop(dso->meth_data);
212         if(ptr == NULL)
213                 {
214                 DSOerr(DSO_F_DLFCN_UNLOAD,DSO_R_NULL_HANDLE);
215                 /* Should push the value back onto the stack in
216                  * case of a retry. */
217                 sk_push(dso->meth_data, (char *)ptr);
218                 return(0);
219                 }
220         /* For now I'm not aware of any errors associated with dlclose() */
221         dlclose(ptr);
222         return(1);
223         }
224
225 static void *dlfcn_bind_var(DSO *dso, const char *symname)
226         {
227         void *ptr, *sym;
228
229         if((dso == NULL) || (symname == NULL))
230                 {
231                 DSOerr(DSO_F_DLFCN_BIND_VAR,ERR_R_PASSED_NULL_PARAMETER);
232                 return(NULL);
233                 }
234         if(sk_num(dso->meth_data) < 1)
235                 {
236                 DSOerr(DSO_F_DLFCN_BIND_VAR,DSO_R_STACK_ERROR);
237                 return(NULL);
238                 }
239         ptr = (void *)sk_value(dso->meth_data, sk_num(dso->meth_data) - 1);
240         if(ptr == NULL)
241                 {
242                 DSOerr(DSO_F_DLFCN_BIND_VAR,DSO_R_NULL_HANDLE);
243                 return(NULL);
244                 }
245         sym = dlsym(ptr, symname);
246         if(sym == NULL)
247                 {
248                 DSOerr(DSO_F_DLFCN_BIND_VAR,DSO_R_SYM_FAILURE);
249                 ERR_add_error_data(4, "symname(", symname, "): ", dlerror());
250                 return(NULL);
251                 }
252         return(sym);
253         }
254
255 static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname)
256         {
257         void *ptr;
258         DSO_FUNC_TYPE sym, *tsym = &sym;
259
260         if((dso == NULL) || (symname == NULL))
261                 {
262                 DSOerr(DSO_F_DLFCN_BIND_FUNC,ERR_R_PASSED_NULL_PARAMETER);
263                 return(NULL);
264                 }
265         if(sk_num(dso->meth_data) < 1)
266                 {
267                 DSOerr(DSO_F_DLFCN_BIND_FUNC,DSO_R_STACK_ERROR);
268                 return(NULL);
269                 }
270         ptr = (void *)sk_value(dso->meth_data, sk_num(dso->meth_data) - 1);
271         if(ptr == NULL)
272                 {
273                 DSOerr(DSO_F_DLFCN_BIND_FUNC,DSO_R_NULL_HANDLE);
274                 return(NULL);
275                 }
276         *(void **)(tsym) = dlsym(ptr, symname);
277         if(sym == NULL)
278                 {
279                 DSOerr(DSO_F_DLFCN_BIND_FUNC,DSO_R_SYM_FAILURE);
280                 ERR_add_error_data(4, "symname(", symname, "): ", dlerror());
281                 return(NULL);
282                 }
283         return(sym);
284         }
285
286 static char *dlfcn_merger(DSO *dso, const char *filespec1,
287         const char *filespec2)
288         {
289         char *merged;
290
291         if(!filespec1 && !filespec2)
292                 {
293                 DSOerr(DSO_F_DLFCN_MERGER,
294                                 ERR_R_PASSED_NULL_PARAMETER);
295                 return(NULL);
296                 }
297         /* If the first file specification is a rooted path, it rules.
298            same goes if the second file specification is missing. */
299         if (!filespec2 || (filespec1 != NULL && filespec1[0] == '/'))
300                 {
301                 merged = OPENSSL_malloc(strlen(filespec1) + 1);
302                 if(!merged)
303                         {
304                         DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
305                         return(NULL);
306                         }
307                 strcpy(merged, filespec1);
308                 }
309         /* If the first file specification is missing, the second one rules. */
310         else if (!filespec1)
311                 {
312                 merged = OPENSSL_malloc(strlen(filespec2) + 1);
313                 if(!merged)
314                         {
315                         DSOerr(DSO_F_DLFCN_MERGER,
316                                 ERR_R_MALLOC_FAILURE);
317                         return(NULL);
318                         }
319                 strcpy(merged, filespec2);
320                 }
321         else
322                 /* This part isn't as trivial as it looks.  It assumes that
323                    the second file specification really is a directory, and
324                    makes no checks whatsoever.  Therefore, the result becomes
325                    the concatenation of filespec2 followed by a slash followed
326                    by filespec1. */
327                 {
328                 int spec2len, len;
329
330                 spec2len = strlen(filespec2);
331                 len = spec2len + (filespec1 ? strlen(filespec1) : 0);
332
333                 if(filespec2 && filespec2[spec2len - 1] == '/')
334                         {
335                         spec2len--;
336                         len--;
337                         }
338                 merged = OPENSSL_malloc(len + 2);
339                 if(!merged)
340                         {
341                         DSOerr(DSO_F_DLFCN_MERGER,
342                                 ERR_R_MALLOC_FAILURE);
343                         return(NULL);
344                         }
345                 strcpy(merged, filespec2);
346                 merged[spec2len] = '/';
347                 strcpy(&merged[spec2len + 1], filespec1);
348                 }
349         return(merged);
350         }
351
352 static char *dlfcn_name_converter(DSO *dso, const char *filename)
353         {
354         char *translated;
355         int len, rsize, transform;
356
357         len = strlen(filename);
358         rsize = len + 1;
359         transform = (strstr(filename, "/") == NULL);
360         if(transform)
361                 {
362                 /* We will convert this to "%s.so" or "lib%s.so" */
363                 rsize += 3;     /* The length of ".so" */
364                 if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
365                         rsize += 3; /* The length of "lib" */
366                 }
367         translated = OPENSSL_malloc(rsize);
368         if(translated == NULL)
369                 {
370                 DSOerr(DSO_F_DLFCN_NAME_CONVERTER,
371                                 DSO_R_NAME_TRANSLATION_FAILED);
372                 return(NULL);
373                 }
374         if(transform)
375                 {
376                 if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
377                         sprintf(translated, "lib%s.so", filename);
378                 else
379                         sprintf(translated, "%s.so", filename);
380                 }
381         else
382                 sprintf(translated, "%s", filename);
383         return(translated);
384         }
385
386 #ifdef __sgi
387 /*
388 This is a quote from IRIX manual for dladdr(3c):
389
390      <dlfcn.h> does not contain a prototype for dladdr or definition of
391      Dl_info.  The #include <dlfcn.h>  in the SYNOPSIS line is traditional,
392      but contains no dladdr prototype and no IRIX library contains an
393      implementation.  Write your own declaration based on the code below.
394
395      The following code is dependent on internal interfaces that are not
396      part of the IRIX compatibility guarantee; however, there is no future
397      intention to change this interface, so on a practical level, the code
398      below is safe to use on IRIX.
399 */
400 #define HAVE_DLINFO 1
401 #include <rld_interface.h>
402 #ifndef _RLD_INTERFACE_DLFCN_H_DLADDR
403 #define _RLD_INTERFACE_DLFCN_H_DLADDR
404 typedef struct Dl_info {
405     const char * dli_fname;
406     void       * dli_fbase;
407     const char * dli_sname;
408     void       * dli_saddr;
409     int          dli_version;
410     int          dli_reserved1;
411     long         dli_reserved[4];
412 } Dl_info;
413 #else
414 typedef struct Dl_info Dl_info;
415 #endif
416 #define _RLD_DLADDR             14
417
418 static int dladdr(void *address, Dl_info *dl)
419 {
420         void *v;
421         v = _rld_new_interface(_RLD_DLADDR,address,dl);
422         return (int)v;
423 }
424 #endif /* __sgi */
425
426 static int dlfcn_pathbyaddr(void *addr,char *path,int sz)
427         {
428 #ifdef HAVE_DLINFO
429         Dl_info dli;
430         int len;
431
432         if (addr == NULL)
433                 {
434                 union   { int(*f)(void*,char*,int); void *p; } t =
435                         { dlfcn_pathbyaddr };
436                 addr = t.p;
437                 }
438
439         if (dladdr(addr,&dli))
440                 {
441                 len = (int)strlen(dli.dli_fname);
442                 if (sz <= 0) return len+1;
443                 if (len >= sz) len=sz-1;
444                 memcpy(path,dli.dli_fname,len);
445                 path[len++]=0;
446                 return len;
447                 }
448
449         ERR_add_error_data(4, "dlfcn_pathbyaddr(): ", dlerror());
450 #endif
451         return -1;
452         }
453
454 static void *dlfcn_globallookup(const char *name)
455         {
456         void *ret = NULL,*handle = dlopen(NULL,RTLD_LAZY);
457         
458         if (handle)
459                 {
460                 ret = dlsym(handle,name);
461                 dlclose(handle);
462                 }
463
464         return ret;
465         }
466 #endif /* DSO_DLFCN */