1 /* Return error detail for failing <dlfcn.h> functions.
2    Copyright (C) 1995-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 #include <dlfcn.h>
20 #include <libintl.h>
21 #include <stdbool.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <libc-lock.h>
26 #include <ldsodefs.h>
27 #include <libc-symbols.h>
28 #include <assert.h>
29 #include <dlerror.h>
30 
31 char *
__dlerror(void)32 __dlerror (void)
33 {
34 # ifdef SHARED
35   if (GLRO (dl_dlfcn_hook) != NULL)
36     return GLRO (dl_dlfcn_hook)->dlerror ();
37 # endif
38 
39   struct dl_action_result *result = __libc_dlerror_result;
40 
41   /* No libdl function has been called.  No error is possible.  */
42   if (result == NULL)
43     return NULL;
44 
45   /* For an early malloc failure, clear the error flag and return the
46      error message.  This marks the error as delivered.  */
47   if (result == dl_action_result_malloc_failed)
48     {
49       __libc_dlerror_result = NULL;
50       return (char *) "out of memory";
51     }
52 
53   /* Placeholder object.  This can be observed in a recursive call,
54      e.g. from an ELF constructor.  */
55   if (result->errstring == NULL)
56     return NULL;
57 
58   /* If we have already reported the error, we can free the result and
59      return NULL.  See __libc_dlerror_result_free.  */
60   if (result->returned)
61     {
62       __libc_dlerror_result = NULL;
63       dl_action_result_errstring_free (result);
64       free (result);
65       return NULL;
66     }
67 
68   assert (result->errstring != NULL);
69 
70   /* Create the combined error message.  */
71   char *buf;
72   int n;
73   if (result->errcode == 0)
74     n = __asprintf (&buf, "%s%s%s",
75 		    result->objname,
76 		    result->objname[0] == '\0' ? "" : ": ",
77 		    _(result->errstring));
78   else
79     {
80       __set_errno (result->errcode);
81       n = __asprintf (&buf, "%s%s%s: %m",
82 		      result->objname,
83 		      result->objname[0] == '\0' ? "" : ": ",
84 		      _(result->errstring));
85       /* Set errno again in case asprintf clobbered it.  */
86       __set_errno (result->errcode);
87     }
88 
89   /* Mark the error as delivered.  */
90   result->returned = true;
91 
92   if (n >= 0)
93     {
94       /* Replace the error string with the newly allocated one.  */
95       dl_action_result_errstring_free (result);
96       result->errstring = buf;
97       result->errstring_source = dl_action_result_errstring_local;
98       return buf;
99     }
100   else
101     /* We could not create the combined error message, so use the
102        existing string as a fallback.  */
103     return result->errstring;
104 }
105 versioned_symbol (libc, __dlerror, dlerror, GLIBC_2_34);
106 
107 #if OTHER_SHLIB_COMPAT (libdl, GLIBC_2_0, GLIBC_2_34)
108 compat_symbol (libdl, __dlerror, dlerror, GLIBC_2_0);
109 #endif
110 
111 int
_dlerror_run(void (* operate)(void *),void * args)112 _dlerror_run (void (*operate) (void *), void *args)
113 {
114   struct dl_action_result *result = __libc_dlerror_result;
115   if (result != NULL)
116     {
117       if (result == dl_action_result_malloc_failed)
118 	{
119 	  /* Clear the previous error.  */
120 	  __libc_dlerror_result = NULL;
121 	  result = NULL;
122 	}
123       else
124 	{
125 	  /* There is an existing object.  Free its error string, but
126 	     keep the object.  */
127 	  dl_action_result_errstring_free (result);
128 	  /* Mark the object as not containing an error.  This ensures
129 	     that call to dlerror from, for example, an ELF
130 	     constructor will not notice this result object.  */
131 	  result->errstring = NULL;
132 	}
133     }
134 
135   const char *objname;
136   const char *errstring;
137   bool malloced;
138   int errcode = GLRO (dl_catch_error) (&objname, &errstring, &malloced,
139 				       operate, args);
140 
141   /* ELF constructors or destructors may have indirectly altered the
142      value of __libc_dlerror_result, therefore reload it.  */
143   result = __libc_dlerror_result;
144 
145   if (errstring == NULL)
146     {
147       /* There is no error.  We no longer need the result object if it
148 	 does not contain an error.  However, a recursive call may
149 	 have added an error even if this call did not cause it.  Keep
150 	 the other error.  */
151       if (result != NULL && result->errstring == NULL)
152 	{
153 	  __libc_dlerror_result = NULL;
154 	  free (result);
155 	}
156       return 0;
157     }
158   else
159     {
160       /* A new error occurred.  Check if a result object has to be
161 	 allocated.  */
162       if (result == NULL || result == dl_action_result_malloc_failed)
163 	{
164 	  /* Allocating storage for the error message after the fact
165 	     is not ideal.  But this avoids an infinite recursion in
166 	     case malloc itself calls libdl functions (without
167 	     triggering errors).  */
168 	  result = malloc (sizeof (*result));
169 	  if (result == NULL)
170 	    {
171 	      /* Assume that the dlfcn failure was due to a malloc
172 		 failure, too.  */
173 	      if (malloced)
174 		dl_error_free ((char *) errstring);
175 	      __libc_dlerror_result = dl_action_result_malloc_failed;
176 	      return 1;
177 	    }
178 	  __libc_dlerror_result = result;
179 	}
180       else
181 	/* Deallocate the existing error message from a recursive
182 	   call, but reuse the result object.  */
183 	dl_action_result_errstring_free (result);
184 
185       result->errcode = errcode;
186       result->objname = objname;
187       result->errstring = (char *) errstring;
188       result->returned = false;
189       /* In case of an error, the malloced flag indicates whether the
190 	 error string is constant or not.  */
191       if (malloced)
192 	result->errstring_source = dl_action_result_errstring_rtld;
193       else
194 	result->errstring_source = dl_action_result_errstring_constant;
195 
196       return 1;
197     }
198 }
199