Revert "elf: Refuse to dlopen PIE objects [BZ #24323]"
[platform/upstream/glibc.git] / elf / dl-hwcaps.c
1 /* Hardware capability support for run-time dynamic loader.
2    Copyright (C) 2012-2024 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 <assert.h>
20 #include <elf.h>
21 #include <errno.h>
22 #include <libintl.h>
23 #include <unistd.h>
24 #include <ldsodefs.h>
25
26 #include <dl-procinfo.h>
27 #include <dl-hwcaps.h>
28
29 /* This is the result of counting the substrings in a colon-separated
30    hwcaps string.  */
31 struct hwcaps_counts
32 {
33   /* Number of substrings.  */
34   size_t count;
35
36   /* Sum of the individual substring lengths (without separators or
37      null terminators).  */
38   size_t total_length;
39
40   /* Maximum length of an individual substring.  */
41   size_t maximum_length;
42 };
43
44 /* Update *COUNTS according to the contents of HWCAPS.  Skip over
45    entries whose bit is not set in MASK.  */
46 static void
47 update_hwcaps_counts (struct hwcaps_counts *counts, const char *hwcaps,
48                       uint32_t bitmask, const char *mask)
49 {
50   struct dl_hwcaps_split_masked sp;
51   _dl_hwcaps_split_masked_init (&sp, hwcaps, bitmask, mask);
52   while (_dl_hwcaps_split_masked (&sp))
53     {
54       ++counts->count;
55       counts->total_length += sp.split.length;
56       if (sp.split.length > counts->maximum_length)
57         counts->maximum_length = sp.split.length;
58     }
59 }
60
61 /* State for copy_hwcaps.  Must be initialized to point to
62    the storage areas for the array and the strings themselves.  */
63 struct copy_hwcaps
64 {
65   struct r_strlenpair *next_pair;
66   char *next_string;
67 };
68
69 /* Copy HWCAPS into the string pairs and strings, advancing *TARGET.
70    Skip over entries whose bit is not set in MASK.  */
71 static void
72 copy_hwcaps (struct copy_hwcaps *target, const char *hwcaps,
73              uint32_t bitmask, const char *mask)
74 {
75   struct dl_hwcaps_split_masked sp;
76   _dl_hwcaps_split_masked_init (&sp, hwcaps, bitmask, mask);
77   while (_dl_hwcaps_split_masked (&sp))
78     {
79       target->next_pair->str = target->next_string;
80       char *slash = __mempcpy (__mempcpy (target->next_string,
81                                           GLIBC_HWCAPS_PREFIX,
82                                           strlen (GLIBC_HWCAPS_PREFIX)),
83                                sp.split.segment, sp.split.length);
84       *slash = '/';
85       target->next_pair->len
86         = strlen (GLIBC_HWCAPS_PREFIX) + sp.split.length + 1;
87       ++target->next_pair;
88       target->next_string = slash + 1;
89     }
90 }
91
92 struct dl_hwcaps_priority *_dl_hwcaps_priorities;
93 uint32_t _dl_hwcaps_priorities_length;
94
95 /* Allocate _dl_hwcaps_priorities and fill it with data.  */
96 static void
97 compute_priorities (size_t total_count, const char *prepend,
98                     uint32_t bitmask, const char *mask)
99 {
100   _dl_hwcaps_priorities = malloc (total_count
101                                   * sizeof (*_dl_hwcaps_priorities));
102   if (_dl_hwcaps_priorities == NULL)
103     _dl_signal_error (ENOMEM, NULL, NULL,
104                       N_("cannot create HWCAP priorities"));
105   _dl_hwcaps_priorities_length = total_count;
106
107   /* First the prepended subdirectories.  */
108   size_t i = 0;
109   {
110     struct dl_hwcaps_split sp;
111     _dl_hwcaps_split_init (&sp, prepend);
112     while (_dl_hwcaps_split (&sp))
113       {
114         _dl_hwcaps_priorities[i].name = sp.segment;
115         _dl_hwcaps_priorities[i].name_length = sp.length;
116         _dl_hwcaps_priorities[i].priority = i + 1;
117         ++i;
118       }
119   }
120
121   /* Then the built-in subdirectories that are actually active.  */
122   {
123     struct dl_hwcaps_split_masked sp;
124     _dl_hwcaps_split_masked_init (&sp, _dl_hwcaps_subdirs, bitmask, mask);
125     while (_dl_hwcaps_split_masked (&sp))
126       {
127         _dl_hwcaps_priorities[i].name = sp.split.segment;
128         _dl_hwcaps_priorities[i].name_length = sp.split.length;
129         _dl_hwcaps_priorities[i].priority = i + 1;
130         ++i;
131       }
132   }
133   assert (i == total_count);
134 }
135
136 /* Sort the _dl_hwcaps_priorities array by name.  */
137 static void
138 sort_priorities_by_name (void)
139 {
140   /* Insertion sort.  There is no need to link qsort into the dynamic
141      loader for such a short array.  */
142   for (size_t i = 1; i < _dl_hwcaps_priorities_length; ++i)
143     for (size_t j = i; j > 0; --j)
144       {
145         struct dl_hwcaps_priority *previous = _dl_hwcaps_priorities + j - 1;
146         struct dl_hwcaps_priority *current = _dl_hwcaps_priorities + j;
147
148         /* Bail out if current is greater or equal to the previous
149            value.  */
150         uint32_t to_compare;
151         if (current->name_length < previous->name_length)
152           to_compare = current->name_length;
153         else
154           to_compare = previous->name_length;
155         int cmp = memcmp (current->name, previous->name, to_compare);
156         if (cmp > 0
157             || (cmp == 0 && current->name_length >= previous->name_length))
158           break;
159
160         /* Swap *previous and *current.  */
161         struct dl_hwcaps_priority tmp = *previous;
162         *previous = *current;
163         *current = tmp;
164       }
165 }
166
167 /* Return an array of useful/necessary hardware capability names.  */
168 const struct r_strlenpair *
169 _dl_important_hwcaps (const char *glibc_hwcaps_prepend,
170                       const char *glibc_hwcaps_mask,
171                       size_t *sz, size_t *max_capstrlen)
172 {
173   /* glibc-hwcaps subdirectories.  */
174   uint32_t hwcaps_subdirs_active = _dl_hwcaps_subdirs_active ();
175   struct hwcaps_counts hwcaps_counts =  { 0, };
176   update_hwcaps_counts (&hwcaps_counts, glibc_hwcaps_prepend, -1, NULL);
177   update_hwcaps_counts (&hwcaps_counts, _dl_hwcaps_subdirs,
178                         hwcaps_subdirs_active, glibc_hwcaps_mask);
179   compute_priorities (hwcaps_counts.count, glibc_hwcaps_prepend,
180                       hwcaps_subdirs_active, glibc_hwcaps_mask);
181   sort_priorities_by_name ();
182
183   /* Each hwcaps subdirectory has a GLIBC_HWCAPS_PREFIX string prefix
184      and a "/" suffix once stored in the result.  */
185   hwcaps_counts.maximum_length += strlen (GLIBC_HWCAPS_PREFIX) + 1;
186   size_t total = (hwcaps_counts.count * (strlen (GLIBC_HWCAPS_PREFIX) + 1)
187                   + hwcaps_counts.total_length);
188
189   *sz = hwcaps_counts.count + 1;
190
191   /* This is the overall result.  */
192   struct r_strlenpair *overall_result
193     = malloc (*sz * sizeof (*overall_result) + total);
194   if (overall_result == NULL)
195     _dl_signal_error (ENOMEM, NULL, NULL,
196                       N_("cannot create capability list"));
197
198   /* Fill in the glibc-hwcaps subdirectories.  */
199   {
200     struct copy_hwcaps target;
201     target.next_pair = overall_result;
202     target.next_string = (char *) (overall_result + *sz);
203     copy_hwcaps (&target, glibc_hwcaps_prepend, -1, NULL);
204     copy_hwcaps (&target, _dl_hwcaps_subdirs,
205                  hwcaps_subdirs_active, glibc_hwcaps_mask);
206
207     /* Append an empty entry for the base directory itself.  */
208     target.next_pair->str = target.next_string;
209     target.next_pair->len = 0;
210   }
211
212   /* The maximum string length.  */
213   *max_capstrlen = hwcaps_counts.maximum_length;
214
215   return overall_result;
216 }