xref: /illumos-gate/usr/src/cmd/sgs/rtld/common/cap.c (revision 47842382d52f28aa3173aa6b511781c322ccb6a2)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include	<sys/types.h>
28 #include	<sys/mman.h>
29 #include	<dirent.h>
30 #include	<stdio.h>
31 #include	<stdlib.h>
32 #include	<string.h>
33 #include	<limits.h>
34 #include	<debug.h>
35 #include	<conv.h>
36 #include	"_rtld.h"
37 #include	"_audit.h"
38 #include	"msg.h"
39 
40 /*
41  * qsort(3c) comparison function.
42  */
43 static int
44 compare(const void *fdesc1, const void *fdesc2)
45 {
46 	Xword	hwcap1 = ((Fdesc *)fdesc1)->fd_hwcap;
47 	Xword	hwcap2 = ((Fdesc *)fdesc2)->fd_hwcap;
48 
49 	if (hwcap1 && (hwcap2 == 0))
50 		return (-1);
51 	if ((hwcap1 == 0) && hwcap2)
52 		return (1);
53 	if ((hwcap1 == 0) && (hwcap2 == 0))
54 		return (0);
55 
56 	if (hwcap1 > hwcap2)
57 		return (-1);
58 	if (hwcap1 < hwcap2)
59 		return (1);
60 	return (0);
61 }
62 
63 /*
64  * Process any hardware capabilities.
65  */
66 int
67 hwcap_check(Xword val, Rej_desc *rej)
68 {
69 	Xword	mval;
70 
71 	/*
72 	 * Ensure that the kernel can cope with the required capabilities.
73 	 */
74 	if ((rtld_flags2 & RT_FL2_HWCAP) && ((mval = (val & ~hwcap)) != 0)) {
75 		static Conv_cap_val_hw1_buf_t	cap_buf;
76 
77 		rej->rej_type = SGS_REJ_HWCAP_1;
78 		rej->rej_str = conv_cap_val_hw1(mval, M_MACH, 0, &cap_buf);
79 		return (0);
80 	}
81 	return (1);
82 }
83 
84 /*
85  * Process any software capabilities.
86  */
87 /* ARGSUSED0 */
88 int
89 sfcap_check(Xword val, Rej_desc *rej)
90 {
91 #if	defined(_ELF64)
92 	/*
93 	 * A 64-bit executable that started the process can be restricted to a
94 	 * 32-bit address space.  A 64-bit dependency that is restricted to a
95 	 * 32-bit address space can not be loaded unless the executable has
96 	 * established this requirement.
97 	 */
98 	if ((val & SF1_SUNW_ADDR32) && ((rtld_flags2 & RT_FL2_ADDR32) == 0)) {
99 		static Conv_cap_val_sf1_buf_t	cap_buf;
100 
101 		rej->rej_type = SGS_REJ_SFCAP_1;
102 		rej->rej_str =
103 		    conv_cap_val_sf1(SF1_SUNW_ADDR32, M_MACH, 0, &cap_buf);
104 		return (0);
105 	}
106 #endif
107 	return (1);
108 }
109 
110 /*
111  * When $HWCAP is used to represent dependencies, take the associated directory
112  * and analyze all the files it contains.
113  */
114 static int
115 hwcap_dir(Alist **fdalpp, Lm_list *lml, const char *dname, Rt_map *clmp,
116     uint_t flags, Rej_desc *rej, int *in_nfavl)
117 {
118 	char		path[PATH_MAX], *dst;
119 	const char	*src;
120 	DIR		*dir;
121 	struct dirent	*dirent;
122 	Alist		*fdalp = NULL;
123 	int		error = 0;
124 
125 	/*
126 	 * Access the directory in preparation for reading its entries.  If
127 	 * successful, establish the initial pathname.
128 	 */
129 	if ((dir = opendir(dname)) == NULL) {
130 		Rej_desc	_rej = { 0 };
131 
132 		_rej.rej_type = SGS_REJ_STR;
133 		_rej.rej_name = dname;
134 		_rej.rej_str = strerror(errno);
135 		DBG_CALL(Dbg_file_rejected(lml, &_rej, M_MACH));
136 		rejection_inherit(rej, &_rej);
137 		return (0);
138 	}
139 
140 	for (dst = path, src = dname; *src; dst++, src++)
141 		*dst = *src;
142 	*dst++ = '/';
143 
144 	/*
145 	 * Read each entry from the directory and determine whether it is a
146 	 * valid ELF file.
147 	 */
148 	while ((dirent = readdir(dir)) != NULL) {
149 		const char	*file = dirent->d_name;
150 		char		*_dst;
151 		Fdesc		fd = { 0 };
152 		Rej_desc	_rej = { 0 };
153 		Pdesc		pd = { 0 };
154 
155 		/*
156 		 * Ignore "." and ".." entries.
157 		 */
158 		if ((file[0] == '.') && ((file[1] == '\0') ||
159 		    ((file[1] == '.') && (file[2] == '\0'))))
160 			continue;
161 
162 		/*
163 		 * Complete the full pathname.
164 		 */
165 		for (_dst = dst, src = file, file = dst; *src; _dst++, src++)
166 			*_dst = *src;
167 		*_dst = '\0';
168 
169 		/*
170 		 * Trace the inspection of this file, and determine any
171 		 * auditor substitution.
172 		 */
173 		pd.pd_pname = path;
174 		pd.pd_flags = PD_FLG_PNSLASH;
175 
176 		if (load_trace(lml, &pd, clmp, &fd) == NULL)
177 			continue;
178 
179 		/*
180 		 * Note, all directory entries are processed by find_path(),
181 		 * even entries that are directories themselves.  This single
182 		 * point for control keeps the number of stat()'s down, and
183 		 * provides a single point for error diagnostics.
184 		 */
185 		if (find_path(lml, clmp, flags, &fd, &_rej, in_nfavl) == 0) {
186 			rejection_inherit(rej, &_rej);
187 			continue;
188 		}
189 
190 		DBG_CALL(Dbg_cap_hw_candidate(lml, fd.fd_nname));
191 
192 		/*
193 		 * If this object has already been loaded, obtain the hardware
194 		 * capabilities for later sorting.  Otherwise we have a new
195 		 * candidate.
196 		 */
197 		if (fd.fd_lmp)
198 			fd.fd_hwcap = HWCAP(fd.fd_lmp);
199 
200 		if (alist_append(&fdalp, &fd, sizeof (Fdesc),
201 		    AL_CNT_HWCAP) == NULL) {
202 			error = 1;
203 			break;
204 		}
205 	}
206 	(void) closedir(dir);
207 
208 	/*
209 	 * If no objects have been found, we're done.  Also, if an allocation
210 	 * error occurred while processing any object, remove any objects that
211 	 * had already been added to the list and return.
212 	 */
213 	if ((fdalp == NULL) || error) {
214 		if (fdalp)
215 			free(fdalp);
216 		return (0);
217 	}
218 
219 	/*
220 	 * Having processed and retained all candidates from this directory,
221 	 * sort them, based on the precedence of their hardware capabilities.
222 	 */
223 	qsort(fdalp->al_data, fdalp->al_nitems, fdalp->al_size, compare);
224 
225 	*fdalpp = fdalp;
226 	return (1);
227 }
228 
229 int
230 hwcap_filtees(Alist **alpp, Aliste oidx, const char *dir, Aliste nlmco,
231     Lm_cntl *nlmc, Rt_map *flmp, const char *ref, int mode, uint_t flags,
232     int *in_nfavl)
233 {
234 	Alist		*fdalp = NULL;
235 	Aliste		idx;
236 	Fdesc		*fdp;
237 	Lm_list		*lml = LIST(flmp);
238 	int		unused = 0;
239 	Rej_desc	rej = { 0 };
240 
241 	if (hwcap_dir(&fdalp, lml, dir, flmp, flags, &rej, in_nfavl) == 0)
242 		return (0);
243 
244 	/*
245 	 * Now complete the mapping of each of the ordered objects, adding
246 	 * each object to a new pathname descriptor.
247 	 */
248 	for (ALIST_TRAVERSE(fdalp, idx, fdp)) {
249 		Rt_map	*nlmp;
250 		Grp_hdl	*ghp = 0;
251 		Pdesc	*pdp;
252 		int	audit = 0;
253 
254 		if (unused)
255 			continue;
256 
257 		/*
258 		 * Complete mapping the file, obtaining a handle, and continue
259 		 * to analyze the object, establishing dependencies and
260 		 * relocating.  Remove the file descriptor at this point, as it
261 		 * is no longer required.
262 		 */
263 		DBG_CALL(Dbg_file_filtee(lml, NAME(flmp), fdp->fd_nname, 0));
264 
265 		nlmp = load_path(lml, nlmco, flmp, mode,
266 		    (flags | FLG_RT_HANDLE), &ghp, fdp, &rej, in_nfavl);
267 		if (nlmp == 0)
268 			continue;
269 
270 		/*
271 		 * Create a new pathname descriptor to represent this filtee,
272 		 * and insert this descriptor in the Alist following the
273 		 * hardware descriptor that seeded this processing.
274 		 * capability directory).
275 		 */
276 		if ((pdp = alist_insert(alpp, 0, sizeof (Pdesc),
277 		    AL_CNT_FILTEES, ++oidx)) == NULL) {
278 			if (ghp)
279 				remove_lmc(lml, flmp, nlmc, nlmco, NAME(nlmp));
280 			return (0);
281 		}
282 
283 		pdp->pd_pname = NAME(nlmp);
284 		pdp->pd_plen = strlen(NAME(nlmp));
285 
286 		/*
287 		 * Establish the filter handle to prevent any recursion.
288 		 */
289 		if (nlmp && ghp) {
290 			ghp->gh_flags |= GPH_FILTEE;
291 			pdp->pd_info = (void *)ghp;
292 		}
293 
294 		/*
295 		 * Audit the filter/filtee established.  A return of 0
296 		 * indicates the auditor wishes to ignore this filtee.
297 		 */
298 		if (nlmp && (lml->lm_tflags | FLAGS1(flmp)) &
299 		    LML_TFLG_AUD_OBJFILTER) {
300 			if (audit_objfilter(flmp, ref, nlmp, 0) == 0) {
301 				audit = 1;
302 				nlmp = 0;
303 			}
304 		}
305 
306 		/*
307 		 * Finish processing the objects associated with this request.
308 		 */
309 		if (nlmp && ghp && (((nlmp = analyze_lmc(lml, nlmco, nlmp,
310 		    in_nfavl)) == NULL) ||
311 		    (relocate_lmc(lml, nlmco, flmp, nlmp, in_nfavl) == 0)))
312 			nlmp = NULL;
313 
314 		/*
315 		 * If the filtee has been successfully processed, then create
316 		 * an association between the filter and the filtee.  This
317 		 * association provides sufficient information to tear down the
318 		 * filter and filtee if necessary.
319 		 */
320 		DBG_CALL(Dbg_file_hdl_title(DBG_HDL_ADD));
321 		if (nlmp && ghp && (hdl_add(ghp, flmp, GPD_FILTER) == 0))
322 			nlmp = NULL;
323 
324 		/*
325 		 * If this object is marked an end-filtee, we're done.
326 		 */
327 		if (nlmp && ghp && (FLAGS1(nlmp) & FL1_RT_ENDFILTE))
328 			unused = 1;
329 
330 		/*
331 		 * If this filtee loading has failed, generate a diagnostic.
332 		 * Null out the path name descriptor entry, and continue the
333 		 * search.
334 		 */
335 		if (nlmp == NULL) {
336 			DBG_CALL(Dbg_file_filtee(lml, 0, pdp->pd_pname, audit));
337 
338 			/*
339 			 * If attempting to load this filtee required a new
340 			 * link-map control list to which this request has
341 			 * added objects, then remove all the objects that
342 			 * have been associated to this request.
343 			 */
344 			if (nlmc && nlmc->lc_head)
345 				remove_lmc(lml, flmp, nlmc, nlmco,
346 				    pdp->pd_pname);
347 
348 			pdp->pd_plen = 0;
349 			pdp->pd_info = 0;
350 		}
351 	}
352 
353 	free(fdalp);
354 	return (1);
355 }
356 
357 /*
358  * Load an individual hardware capabilities object.
359  */
360 Rt_map *
361 load_hwcap(Lm_list *lml, Aliste lmco, const char *dir, Rt_map *clmp,
362     uint_t mode, uint_t flags, Grp_hdl **hdl, Rej_desc *rej, int *in_nfavl)
363 {
364 	Alist	*fdalp = NULL;
365 	Aliste	idx;
366 	Fdesc	*fdp;
367 	int	found = 0;
368 	Rt_map	*lmp = 0;
369 
370 	/*
371 	 * Obtain the sorted list of hardware capabilities objects available.
372 	 */
373 	if (hwcap_dir(&fdalp, lml, dir, clmp, flags, rej, in_nfavl) == 0)
374 		return (NULL);
375 
376 	/*
377 	 * From the list of hardware capability objects, use the first and
378 	 * discard the rest.
379 	 */
380 	for (ALIST_TRAVERSE(fdalp, idx, fdp)) {
381 		Fdesc	fd = *fdp;
382 
383 		if ((found == 0) && ((lmp = load_path(lml, lmco, clmp, mode,
384 		    flags, hdl, &fd, rej, in_nfavl)) != 0))
385 			found++;
386 	}
387 
388 	free(fdalp);
389 	return (lmp);
390 }
391