rtld: properly handle root directory in load path (bug 30435)
[platform/upstream/glibc.git] / elf / dl-catch.c
1 /* Exception handling in the dynamic linker.
2    Copyright (C) 1995-2023 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 <libintl.h>
20 #include <setjmp.h>
21 #include <stdbool.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <ldsodefs.h>
26 #include <stdio.h>
27 #include <tls.h>
28
29 /* This structure communicates state between _dl_catch_error and
30    _dl_signal_error.  */
31 struct rtld_catch
32   {
33     struct dl_exception *exception; /* The exception data is stored there.  */
34     volatile int *errcode;      /* Return value of _dl_signal_error.  */
35     jmp_buf env;                /* longjmp here on error.  */
36   };
37
38 /* Multiple threads at once can use the `_dl_catch_error' function.
39    The calls can come from `_dl_map_object_deps', `_dlerror_run', or
40    from any of the libc functionality which loads dynamic objects
41    (NSS, iconv).  Therefore we have to be prepared to save the state
42    in thread-local memory.  We use THREAD_GETMEM and THREAD_SETMEM
43    instead of ELF TLS because ELF TLS is not available in the dynamic
44    loader.  Additionally, the exception handling mechanism must be
45    usable before the TCB has been set up, which is why
46    rtld_catch_notls is used if !__rtld_tls_init_tp_called.  This is
47    not needed for static builds, where initialization completes before
48    static dlopen etc. can be called.  */
49
50 #if IS_IN (rtld)
51 static struct rtld_catch *rtld_catch_notls;
52 #endif
53
54 static struct rtld_catch *
55 get_catch (void)
56 {
57 #if IS_IN (rtld)
58   if (!__rtld_tls_init_tp_called)
59     return rtld_catch_notls;
60   else
61 #endif
62     return THREAD_GETMEM (THREAD_SELF, rtld_catch);
63 }
64
65 static void
66 set_catch (struct rtld_catch *catch)
67 {
68 #if IS_IN (rtld)
69   if (!__rtld_tls_init_tp_called)
70     rtld_catch_notls = catch;
71   else
72 #endif
73     THREAD_SETMEM (THREAD_SELF, rtld_catch, catch);
74 }
75
76 /* Lossage while resolving the program's own symbols is always fatal.  */
77 static void
78 __attribute__ ((noreturn))
79 fatal_error (int errcode, const char *objname, const char *occasion,
80              const char *errstring)
81 {
82   char buffer[1024];
83   _dl_fatal_printf ("%s: %s: %s%s%s%s%s\n",
84                     RTLD_PROGNAME,
85                     occasion ?: N_("error while loading shared libraries"),
86                     objname, *objname ? ": " : "",
87                     errstring, errcode ? ": " : "",
88                     (errcode
89                      ? __strerror_r (errcode, buffer, sizeof buffer)
90                      : ""));
91 }
92
93 void
94 _dl_signal_exception (int errcode, struct dl_exception *exception,
95                       const char *occasion)
96 {
97   struct rtld_catch *lcatch = get_catch ();
98   if (lcatch != NULL)
99     {
100       *lcatch->exception = *exception;
101       *lcatch->errcode = errcode;
102
103       /* We do not restore the signal mask because none was saved.  */
104       __longjmp (lcatch->env[0].__jmpbuf, 1);
105     }
106   else
107     fatal_error (errcode, exception->objname, occasion, exception->errstring);
108 }
109 rtld_hidden_def (_dl_signal_exception)
110
111 void
112 _dl_signal_error (int errcode, const char *objname, const char *occation,
113                   const char *errstring)
114 {
115   struct rtld_catch *lcatch = get_catch ();
116
117   if (! errstring)
118     errstring = N_("DYNAMIC LINKER BUG!!!");
119
120   if (lcatch != NULL)
121     {
122       _dl_exception_create (lcatch->exception, objname, errstring);
123       *lcatch->errcode = errcode;
124
125       /* We do not restore the signal mask because none was saved.  */
126       __longjmp (lcatch->env[0].__jmpbuf, 1);
127     }
128   else
129     fatal_error (errcode, objname, occation, errstring);
130 }
131 rtld_hidden_def (_dl_signal_error)
132
133 #if IS_IN (rtld)
134 /* This points to a function which is called when a continuable error is
135    received.  Unlike the handling of `catch' this function may return.
136    The arguments will be the `errstring' and `objname'.
137
138    Since this functionality is not used in normal programs (only in ld.so)
139    we do not care about multi-threaded programs here.  We keep this as a
140    global variable.  */
141 static receiver_fct receiver;
142
143 void
144 _dl_signal_cexception (int errcode, struct dl_exception *exception,
145                        const char *occasion)
146 {
147   if (__builtin_expect (GLRO(dl_debug_mask)
148                         & ~(DL_DEBUG_STATISTICS), 0))
149     _dl_debug_printf ("%s: error: %s: %s (%s)\n",
150                       exception->objname, occasion,
151                       exception->errstring, receiver ? "continued" : "fatal");
152
153   if (receiver)
154     {
155       /* We are inside _dl_receive_error.  Call the user supplied
156          handler and resume the work.  The receiver will still be
157          installed.  */
158       (*receiver) (errcode, exception->objname, exception->errstring);
159     }
160   else
161     _dl_signal_exception (errcode, exception, occasion);
162 }
163
164 void
165 _dl_signal_cerror (int errcode, const char *objname, const char *occation,
166                    const char *errstring)
167 {
168   if (__builtin_expect (GLRO(dl_debug_mask)
169                         & ~(DL_DEBUG_STATISTICS), 0))
170     _dl_debug_printf ("%s: error: %s: %s (%s)\n", objname, occation,
171                       errstring, receiver ? "continued" : "fatal");
172
173   if (receiver)
174     {
175       /* We are inside _dl_receive_error.  Call the user supplied
176          handler and resume the work.  The receiver will still be
177          installed.  */
178       (*receiver) (errcode, objname, errstring);
179     }
180   else
181     _dl_signal_error (errcode, objname, occation, errstring);
182 }
183
184 void
185 _dl_receive_error (receiver_fct fct, void (*operate) (void *), void *args)
186 {
187   struct rtld_catch *old_catch = get_catch ();
188   receiver_fct old_receiver = receiver;
189
190   /* Set the new values.  */
191   set_catch (NULL);
192   receiver = fct;
193
194   (*operate) (args);
195
196   set_catch (old_catch);
197   receiver = old_receiver;
198 }
199 #endif
200
201 int
202 _dl_catch_exception (struct dl_exception *exception,
203                      void (*operate) (void *), void *args)
204 {
205   /* If exception is NULL, temporarily disable exception handling.
206      Exceptions during operate (args) are fatal.  */
207   if (exception == NULL)
208     {
209       struct rtld_catch *old_catch = get_catch ();
210       set_catch (NULL);
211       operate (args);
212       /* If we get here, the operation was successful.  */
213       set_catch (old_catch);
214       return 0;
215     }
216
217   /* We need not handle `receiver' since setting a `catch' is handled
218      before it.  */
219
220   /* Only this needs to be marked volatile, because it is the only local
221      variable that gets changed between the setjmp invocation and the
222      longjmp call.  All others are just set here (before setjmp) and read
223      in _dl_signal_error (before longjmp).  */
224   volatile int errcode;
225
226   struct rtld_catch c;
227   /* Don't use an initializer since we don't need to clear C.env.  */
228   c.exception = exception;
229   c.errcode = &errcode;
230
231   struct rtld_catch *old = get_catch ();
232   set_catch (&c);
233
234   /* Do not save the signal mask.  */
235   if (__builtin_expect (__sigsetjmp (c.env, 0), 0) == 0)
236     {
237       (*operate) (args);
238       set_catch (old);
239       *exception = (struct dl_exception) { NULL };
240       return 0;
241     }
242
243   /* We get here only if we longjmp'd out of OPERATE.
244      _dl_signal_exception has already stored values into
245      *EXCEPTION.  */
246   set_catch (old);
247   return errcode;
248 }
249 rtld_hidden_def (_dl_catch_exception)
250
251 int
252 _dl_catch_error (const char **objname, const char **errstring,
253                  bool *mallocedp, void (*operate) (void *), void *args)
254 {
255   struct dl_exception exception;
256   int errorcode = _dl_catch_exception (&exception, operate, args);
257   *objname = exception.objname;
258   *errstring = exception.errstring;
259   *mallocedp = exception.message_buffer == exception.errstring;
260   return errorcode;
261 }