Home | History | Annotate | Download | only in common
      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     Rt_map *flmp, const char *ref, int mode, uint_t flags, int *in_nfavl)
    232 {
    233 	Alist		*fdalp = NULL;
    234 	Aliste		idx;
    235 	Fdesc		*fdp;
    236 	Lm_list		*lml = LIST(flmp);
    237 	int		unused = 0;
    238 	Rej_desc	rej = { 0 };
    239 
    240 	if (hwcap_dir(&fdalp, lml, dir, flmp, flags, &rej, in_nfavl) == 0)
    241 		return (0);
    242 
    243 	/*
    244 	 * Now complete the mapping of each of the ordered objects, adding
    245 	 * each object to a new pathname descriptor.
    246 	 */
    247 	for (ALIST_TRAVERSE(fdalp, idx, fdp)) {
    248 		Rt_map	*nlmp;
    249 		Grp_hdl	*ghp = NULL;
    250 		Pdesc	*pdp;
    251 		int	audit = 0;
    252 
    253 		if (unused)
    254 			continue;
    255 
    256 		/*
    257 		 * Complete mapping the file, obtaining a handle, and continue
    258 		 * to analyze the object, establishing dependencies and
    259 		 * relocating.  Remove the file descriptor at this point, as it
    260 		 * is no longer required.
    261 		 */
    262 		DBG_CALL(Dbg_file_filtee(lml, NAME(flmp), fdp->fd_nname, 0));
    263 
    264 		nlmp = load_path(lml, nlmco, flmp, mode,
    265 		    (flags | FLG_RT_PUBHDL), &ghp, fdp, &rej, in_nfavl);
    266 		if (nlmp == NULL)
    267 			continue;
    268 
    269 		/*
    270 		 * Create a new pathname descriptor to represent this filtee,
    271 		 * and insert this descriptor in the Alist following the
    272 		 * hardware descriptor that seeded this processing.
    273 		 */
    274 		if ((pdp = alist_insert(alpp, 0, sizeof (Pdesc),
    275 		    AL_CNT_FILTEES, ++oidx)) == NULL) {
    276 			if (ghp)
    277 				remove_lmc(lml, flmp, nlmco, NAME(nlmp));
    278 			return (0);
    279 		}
    280 
    281 		pdp->pd_pname = NAME(nlmp);
    282 		pdp->pd_plen = strlen(NAME(nlmp));
    283 
    284 		/*
    285 		 * Establish the filter handle to prevent any recursion.
    286 		 */
    287 		if (nlmp && ghp) {
    288 			ghp->gh_flags |= GPH_FILTEE;
    289 			pdp->pd_info = (void *)ghp;
    290 		}
    291 
    292 		/*
    293 		 * Audit the filter/filtee established.  A return of 0
    294 		 * indicates the auditor wishes to ignore this filtee.
    295 		 */
    296 		if (nlmp && (lml->lm_tflags | FLAGS1(flmp)) &
    297 		    LML_TFLG_AUD_OBJFILTER) {
    298 			if (audit_objfilter(flmp, ref, nlmp, 0) == 0) {
    299 				audit = 1;
    300 				nlmp = NULL;
    301 			}
    302 		}
    303 
    304 		/*
    305 		 * Finish processing the objects associated with this request.
    306 		 */
    307 		if (nlmp && ghp && (((nlmp = analyze_lmc(lml, nlmco, nlmp,
    308 		    in_nfavl)) == NULL) ||
    309 		    (relocate_lmc(lml, nlmco, flmp, nlmp, in_nfavl) == 0)))
    310 			nlmp = NULL;
    311 
    312 		/*
    313 		 * If the filtee has been successfully processed, then create
    314 		 * an association between the filter and the filtee.  This
    315 		 * association provides sufficient information to tear down the
    316 		 * filter and filtee if necessary.
    317 		 */
    318 		DBG_CALL(Dbg_file_hdl_title(DBG_HDL_ADD));
    319 		if (nlmp && ghp &&
    320 		    (hdl_add(ghp, flmp, GPD_FILTER, NULL) == NULL))
    321 			nlmp = NULL;
    322 
    323 		/*
    324 		 * If this object is marked an end-filtee, we're done.
    325 		 */
    326 		if (nlmp && ghp && (FLAGS1(nlmp) & FL1_RT_ENDFILTE))
    327 			unused = 1;
    328 
    329 		/*
    330 		 * If this filtee loading has failed, generate a diagnostic.
    331 		 * Null out the path name descriptor entry, and continue the
    332 		 * search.
    333 		 */
    334 		if (nlmp == NULL) {
    335 			DBG_CALL(Dbg_file_filtee(lml, 0, pdp->pd_pname, audit));
    336 
    337 			/*
    338 			 * If attempting to load this filtee required a new
    339 			 * link-map control list to which this request has
    340 			 * added objects, then remove all the objects that
    341 			 * have been associated to this request.
    342 			 */
    343 			if (nlmco != ALIST_OFF_DATA)
    344 				remove_lmc(lml, flmp, nlmco, pdp->pd_pname);
    345 
    346 			pdp->pd_plen = 0;
    347 			pdp->pd_info = NULL;
    348 		}
    349 	}
    350 
    351 	free(fdalp);
    352 	return (1);
    353 }
    354 
    355 /*
    356  * Load an individual hardware capabilities object.
    357  */
    358 Rt_map *
    359 load_hwcap(Lm_list *lml, Aliste lmco, const char *dir, Rt_map *clmp,
    360     uint_t mode, uint_t flags, Grp_hdl **hdl, Rej_desc *rej, int *in_nfavl)
    361 {
    362 	Alist	*fdalp = NULL;
    363 	Aliste	idx;
    364 	Fdesc	*fdp;
    365 	int	found = 0;
    366 	Rt_map	*lmp = NULL;
    367 
    368 	/*
    369 	 * Obtain the sorted list of hardware capabilities objects available.
    370 	 */
    371 	if (hwcap_dir(&fdalp, lml, dir, clmp, flags, rej, in_nfavl) == 0)
    372 		return (NULL);
    373 
    374 	/*
    375 	 * From the list of hardware capability objects, use the first and
    376 	 * discard the rest.
    377 	 */
    378 	for (ALIST_TRAVERSE(fdalp, idx, fdp)) {
    379 		Fdesc	fd = *fdp;
    380 
    381 		if ((found == 0) && ((lmp = load_path(lml, lmco, clmp, mode,
    382 		    flags, hdl, &fd, rej, in_nfavl)) != NULL))
    383 			found++;
    384 	}
    385 
    386 	free(fdalp);
    387 	return (lmp);
    388 }
    389