xref: /illumos-gate/usr/src/cmd/sgs/rtld/common/cap.c (revision e11c3f44)
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 2008 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 	ulong_t	hwcap1 = ((Fdesc *)fdesc1)->fd_fmap.fm_hwptr;
47 	ulong_t	hwcap2 = ((Fdesc *)fdesc2)->fd_fmap.fm_hwptr;
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 and software capabilities.
65  */
66 int
67 cap_check(Rej_desc *rej, Ehdr *ehdr)
68 {
69 	Cap	*cptr;
70 	Phdr	*phdr;
71 	int	cnt;
72 
73 	/* LINTED */
74 	phdr = (Phdr *)((char *)ehdr + ehdr->e_phoff);
75 	for (cnt = 0; cnt < ehdr->e_phnum; cnt++, phdr++) {
76 		Lword	val;
77 
78 		if (phdr->p_type != PT_SUNWCAP)
79 			continue;
80 
81 		/* LINTED */
82 		for (cptr = (Cap *)((char *)ehdr + phdr->p_offset);
83 		    (cptr->c_tag != CA_SUNW_NULL); cptr++) {
84 
85 			if ((cptr->c_tag == CA_SUNW_HW_1) &&
86 			    (rtld_flags2 & RT_FL2_HWCAP)) {
87 				/*
88 				 * If this object defines a set of hardware
89 				 * capability requirements, ensure the kernel
90 				 * can cope with them.
91 				 */
92 				if ((val = (cptr->c_un.c_val & ~hwcap)) != 0) {
93 					static Conv_cap_val_hw1_buf_t cap_buf;
94 
95 					rej->rej_type = SGS_REJ_HWCAP_1;
96 					rej->rej_str =
97 					    conv_cap_val_hw1(val, M_MACH, 0,
98 					    &cap_buf);
99 					return (0);
100 				}
101 
102 				/*
103 				 * Retain this hardware capabilities value for
104 				 * possible later inspection should this object
105 				 * be processed as a filtee.
106 				 */
107 				fmap->fm_hwptr = cptr->c_un.c_val;
108 
109 			}
110 #if	defined(_ELF64)
111 			if (cptr->c_tag == CA_SUNW_SF_1) {
112 				/*
113 				 * A 64-bit executable that started the process
114 				 * can be restricted to a 32-bit address space.
115 				 * A 64-bit dependency that is restricted to a
116 				 * 32-bit address space can not be loaded unless
117 				 * the executable has established this
118 				 * requirement.
119 				 */
120 				if ((cptr->c_un.c_val & SF1_SUNW_ADDR32) &&
121 				    ((rtld_flags2 & RT_FL2_ADDR32) == 0)) {
122 					static Conv_cap_val_sf1_buf_t cap_buf;
123 
124 					rej->rej_type = SGS_REJ_SFCAP_1;
125 					rej->rej_str =
126 					    conv_cap_val_sf1(SF1_SUNW_ADDR32,
127 					    M_MACH, 0, &cap_buf);
128 					return (0);
129 				}
130 			}
131 #endif
132 		}
133 	}
134 	return (1);
135 }
136 
137 static void
138 remove_fdesc(Fdesc *fdp)
139 {
140 #if	defined(MAP_ALIGN)
141 	if (fdp->fd_fmap.fm_maddr &&
142 	    ((fdp->fd_fmap.fm_mflags & MAP_ALIGN) == 0)) {
143 #else
144 	if (fdp->fd_fmap.fm_maddr) {
145 #endif
146 		(void) munmap(fdp->fd_fmap.fm_maddr, fdp->fd_fmap.fm_msize);
147 
148 		/*
149 		 * Note, this file descriptor might be duplicating information
150 		 * from the global fmap descriptor.  If so, clean up the global
151 		 * descriptor to prevent a duplicate (unnecessary) unmap.
152 		 */
153 		if (fmap->fm_maddr == fdp->fd_fmap.fm_maddr) {
154 			fmap->fm_maddr = 0;
155 			fmap_setup();
156 		}
157 	}
158 	if (fdp->fd_fd)
159 		(void) close(fdp->fd_fd);
160 
161 	if ((fdp->fd_flags & FLG_FD_ALTER) == 0) {
162 		if (fdp->fd_pname && (fdp->fd_pname != fdp->fd_nname))
163 			free((void *)fdp->fd_pname);
164 		if (fdp->fd_nname)
165 			free((void *)fdp->fd_nname);
166 	}
167 }
168 
169 /*
170  * When $HWCAP is used to represent dependencies, take the associated directory
171  * and analyze all the files it contains.
172  */
173 static int
174 hwcap_dir(Alist **fdalpp, Lm_list *lml, const char *name, Rt_map *clmp,
175     uint_t flags, Rej_desc *rej, int *in_nfavl)
176 {
177 	char		path[PATH_MAX], *dst;
178 	const char	*src;
179 	DIR		*dir;
180 	struct dirent	*dirent;
181 	Aliste		idx;
182 	Alist		*fdalp = NULL;
183 	Fdesc		*fdp;
184 	int		error = 0;
185 
186 	/*
187 	 * Access the directory in preparation for reading its entries.  If
188 	 * successful, establish the initial pathname.
189 	 */
190 	if ((dir = opendir(name)) == 0) {
191 		Rej_desc	_rej = { 0 };
192 
193 		_rej.rej_type = SGS_REJ_STR;
194 		_rej.rej_name = name;
195 		_rej.rej_str = strerror(errno);
196 		DBG_CALL(Dbg_file_rejected(lml, &_rej, M_MACH));
197 		rejection_inherit(rej, &_rej);
198 		return (0);
199 	}
200 
201 	for (dst = path, src = name; *src; dst++, src++)
202 		*dst = *src;
203 	*dst++ = '/';
204 
205 	/*
206 	 * Read each entry from the directory and determine whether it is a
207 	 * valid ELF file.
208 	 */
209 	while ((dirent = readdir(dir)) != NULL) {
210 		const char	*file = dirent->d_name, *oname;
211 		char		*_dst;
212 		Fdesc		fdesc = { 0 };
213 		Rej_desc	_rej = { 0 };
214 
215 		/*
216 		 * Ignore "." and ".." entries.
217 		 */
218 		if ((file[0] == '.') && ((file[1] == '\0') ||
219 		    ((file[1] == '.') && (file[2] == '\0'))))
220 			continue;
221 
222 		/*
223 		 * Complete the full pathname, and verify its usability.  Note,
224 		 * an auditor can supply an alternative name.
225 		 */
226 		for (_dst = dst, src = file, file = dst; *src; _dst++, src++)
227 			*_dst = *src;
228 		*_dst = '\0';
229 
230 		if ((oname = strdup(path)) == NULL) {
231 			error = 1;
232 			break;
233 		}
234 
235 		if (load_trace(lml, &oname, clmp) == 0) {
236 			free((void *)oname);
237 			continue;
238 		}
239 		name = oname;
240 
241 		/*
242 		 * Note, all directory entries are processed by find_path(),
243 		 * even entries that are directories themselves.  This single
244 		 * point for control keeps the number of stat()'s down, and
245 		 * provides a single point for error diagnostics.
246 		 */
247 		if (find_path(lml, name, clmp, flags, &fdesc,
248 		    &_rej, in_nfavl) == 0) {
249 			rejection_inherit(rej, &_rej);
250 			if ((rej->rej_name != _rej.rej_name) &&
251 			    (_rej.rej_name == name))
252 				free((void *)name);
253 			continue;
254 		}
255 
256 		DBG_CALL(Dbg_cap_hw_candidate(lml, name));
257 
258 		/*
259 		 * If this object has already been loaded, obtain the hardware
260 		 * capabilities for later sorting.  Otherwise we have a new
261 		 * candidate.
262 		 */
263 		if (fdesc.fd_lmp)
264 			fdesc.fd_fmap.fm_hwptr = HWCAP(fdesc.fd_lmp);
265 		else
266 			fdesc.fd_fmap = *fmap;
267 
268 		if (alist_append(&fdalp, &fdesc, sizeof (Fdesc), 10) == 0) {
269 			remove_fdesc(&fdesc);
270 			error = 1;
271 			break;
272 		}
273 
274 		/*
275 		 * Clear the global file mapping structure so that the mapping
276 		 * for this file won't be overriden.
277 		 */
278 		fmap->fm_mflags = MAP_PRIVATE;
279 		fmap->fm_maddr = 0;
280 		fmap->fm_msize = FMAP_SIZE;
281 		fmap->fm_hwptr = 0;
282 	}
283 	(void) closedir(dir);
284 
285 	/*
286 	 * If no objects have been found, we're done.  Also, if an allocation
287 	 * error occurred while processing any object, remove any objects that
288 	 * had already been added to the list and return.
289 	 */
290 	if ((fdalp == NULL) || error) {
291 		if (fdalp) {
292 			for (ALIST_TRAVERSE(fdalp, idx, fdp))
293 				remove_fdesc(fdp);
294 			free(fdalp);
295 		}
296 		return (0);
297 	}
298 
299 	/*
300 	 * Having processed and retained all candidates from this directory,
301 	 * sort them, based on the precedence of their hardware capabilities.
302 	 */
303 	qsort(fdalp->al_data, fdalp->al_nitems, fdalp->al_size, compare);
304 
305 	*fdalpp = fdalp;
306 	return (1);
307 }
308 
309 static Pnode *
310 _hwcap_filtees(Pnode **pnpp, Aliste nlmco, Lm_cntl *nlmc, Rt_map *flmp,
311     const char *ref, const char *dir, int mode, uint_t flags, int *in_nfavl)
312 {
313 	Alist		*fdalp = NULL;
314 	Aliste		idx;
315 	Pnode		*fpnp = 0, *lpnp, *npnp = (*pnpp)->p_next;
316 	Fdesc		*fdp;
317 	Lm_list		*lml = LIST(flmp);
318 	int		unused = 0;
319 	Rej_desc	rej = { 0 };
320 
321 	if (hwcap_dir(&fdalp, lml, dir, flmp, flags, &rej, in_nfavl) == 0) {
322 		remove_rej(&rej);
323 		return (0);
324 	}
325 
326 	/*
327 	 * Now complete the mapping of each of the ordered objects, adding
328 	 * each object to a new Pnode.
329 	 */
330 	for (ALIST_TRAVERSE(fdalp, idx, fdp)) {
331 		Rt_map	*nlmp;
332 		Grp_hdl	*ghp = 0;
333 		Pnode	*pnp;
334 		int	audit = 0;
335 
336 		if (unused) {
337 			/*
338 			 * Flush out objects remaining.
339 			 */
340 			remove_fdesc(fdp);
341 			continue;
342 		}
343 
344 		/*
345 		 * Complete mapping the file, obtaining a handle, and continue
346 		 * to analyze the object, establishing dependencies and
347 		 * relocating.  Remove the file descriptor at this point, as it
348 		 * is no longer required.
349 		 */
350 		DBG_CALL(Dbg_file_filtee(lml, NAME(flmp), fdp->fd_nname, 0));
351 
352 		nlmp = load_path(lml, nlmco, &fdp->fd_nname, flmp, mode,
353 		    (flags | FLG_RT_HANDLE), &ghp, fdp, &rej, in_nfavl);
354 		remove_fdesc(fdp);
355 		if (nlmp == 0)
356 			continue;
357 
358 		/*
359 		 * Create a new Pnode to represent this filtee, and substitute
360 		 * the calling Pnode (which was used to represent the hardware
361 		 * capability directory).
362 		 */
363 		if ((pnp = calloc(1, sizeof (Pnode))) == 0) {
364 			if (ghp) {
365 				remove_lmc(lml, flmp, nlmc, nlmco,
366 				    fdp->fd_nname);
367 			}
368 			return (0);
369 		}
370 		if ((pnp->p_name = strdup(NAME(nlmp))) == NULL) {
371 			if (ghp) {
372 				remove_lmc(lml, flmp, nlmc, nlmco,
373 				    fdp->fd_nname);
374 			}
375 			free(pnp);
376 			return (0);
377 		}
378 		pnp->p_len = strlen(NAME(nlmp));
379 		pnp->p_info = (void *)ghp;
380 		pnp->p_next = npnp;
381 
382 		if (fpnp == 0) {
383 			Pnode	*opnp = (*pnpp);
384 
385 			/*
386 			 * If this is the first pnode, reuse the original after
387 			 * freeing any of its pathnames.
388 			 */
389 			if (opnp->p_name)
390 				free((void *)opnp->p_name);
391 			if (opnp->p_oname)
392 				free((void *)opnp->p_oname);
393 			*opnp = *pnp;
394 			free((void *)pnp);
395 			fpnp = lpnp = pnp = opnp;
396 		} else {
397 			lpnp->p_next = pnp;
398 			lpnp = pnp;
399 		}
400 
401 		/*
402 		 * Establish the filter handle to prevent any recursion.
403 		 */
404 		if (nlmp && ghp) {
405 			ghp->gh_flags |= GPH_FILTEE;
406 			pnp->p_info = (void *)ghp;
407 		}
408 
409 		/*
410 		 * Audit the filter/filtee established.  A return of 0
411 		 * indicates the auditor wishes to ignore this filtee.
412 		 */
413 		if (nlmp && (lml->lm_tflags | FLAGS1(flmp)) &
414 		    LML_TFLG_AUD_OBJFILTER) {
415 			if (audit_objfilter(flmp, ref, nlmp, 0) == 0) {
416 				audit = 1;
417 				nlmp = 0;
418 			}
419 		}
420 
421 		/*
422 		 * Finish processing the objects associated with this request.
423 		 */
424 		if (nlmp && ghp &&
425 		    ((analyze_lmc(lml, nlmco, nlmp, in_nfavl) == 0) ||
426 		    (relocate_lmc(lml, nlmco, flmp, nlmp, in_nfavl) == 0)))
427 			nlmp = 0;
428 
429 		/*
430 		 * If the filtee has been successfully processed, then create
431 		 * an association between the filter and the filtee.  This
432 		 * association provides sufficient information to tear down the
433 		 * filter and filtee if necessary.
434 		 */
435 		DBG_CALL(Dbg_file_hdl_title(DBG_HDL_ADD));
436 		if (nlmp && ghp && (hdl_add(ghp, flmp, GPD_FILTER) == 0))
437 			nlmp = 0;
438 
439 		/*
440 		 * If this object is marked an end-filtee, we're done.
441 		 */
442 		if (nlmp && ghp && (FLAGS1(nlmp) & FL1_RT_ENDFILTE))
443 			unused = 1;
444 
445 		/*
446 		 * If this filtee loading has failed, generate a diagnostic.
447 		 * Null out the pnode entry, and continue the search.
448 		 */
449 		if (nlmp == 0) {
450 			/*
451 			 * If attempting to load this filtee required a new
452 			 * link-map control list to which this request has
453 			 * added objects, then remove all the objects that
454 			 * have been associated to this request.
455 			 */
456 			if (nlmc && nlmc->lc_head)
457 				remove_lmc(lml, flmp, nlmc, nlmco, pnp->p_name);
458 
459 			DBG_CALL(Dbg_file_filtee(lml, 0, pnp->p_name, audit));
460 
461 			pnp->p_len = 0;
462 			pnp->p_info = 0;
463 		}
464 	}
465 
466 	free(fdalp);
467 	return (fpnp);
468 }
469 
470 Pnode *
471 hwcap_filtees(Pnode **pnpp, Aliste nlmco, Lm_cntl *nlmc, Dyninfo *dip,
472     Rt_map *flmp, const char *ref, int mode, uint_t flags, int *in_nfavl)
473 {
474 	Pnode		*pnp = *pnpp;
475 	const char	*dir = pnp->p_name;
476 	Lm_list		*flml = LIST(flmp);
477 
478 	DBG_CALL(Dbg_cap_hw_filter(flml, dir, flmp));
479 
480 	if ((pnp = _hwcap_filtees(pnpp, nlmco, nlmc, flmp, ref, dir, mode,
481 	    flags, in_nfavl)) != 0)
482 		return (pnp);
483 
484 	/*
485 	 * If no hardware capability filtees have been found, provide suitable
486 	 * diagnostics and mark the incoming Pnode as unused.
487 	 */
488 	if ((flml->lm_flags & LML_FLG_TRC_ENABLE) &&
489 	    (dip->di_flags & FLG_DI_AUXFLTR) && (rtld_flags & RT_FL_WARNFLTR))
490 		(void) printf(MSG_INTL(MSG_LDD_HWCAP_NFOUND), dir);
491 
492 	DBG_CALL(Dbg_cap_hw_filter(flml, dir, 0));
493 
494 	pnp = *pnpp;
495 	pnp->p_len = 0;
496 	return (pnp);
497 }
498 
499 /*
500  * Load an individual hardware capabilities object.
501  */
502 Rt_map *
503 load_hwcap(Lm_list *lml, Aliste lmco, const char *dir, Rt_map *clmp,
504     uint_t mode, uint_t flags, Grp_hdl **hdl, Rej_desc *rej, int *in_nfavl)
505 {
506 	Alist		*fdalp = NULL;
507 	Aliste		idx;
508 	Fdesc		*fdp;
509 	int		found = 0;
510 	Rt_map		*lmp = 0;
511 
512 	/*
513 	 * Obtain the sorted list of hardware capabilites objects available.
514 	 */
515 	if (hwcap_dir(&fdalp, lml, dir, clmp, flags, rej, in_nfavl) == 0)
516 		return (0);
517 
518 	/*
519 	 * From the list of hardware capability objects, use the first and
520 	 * discard the rest.
521 	 */
522 	for (ALIST_TRAVERSE(fdalp, idx, fdp)) {
523 		if ((found == 0) && ((lmp = load_path(lml, lmco, &fdp->fd_nname,
524 		    clmp, mode, flags, hdl, fdp, rej, in_nfavl)) != 0))
525 			found++;
526 
527 		/*
528 		 * Remove the used file descriptor and any objects remaining.
529 		 */
530 		remove_fdesc(fdp);
531 	}
532 
533 	free(fdalp);
534 	return (lmp);
535 }
536