17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 55aefb655Srie * Common Development and Distribution License (the "License"). 65aefb655Srie * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 21fb1354edSrie 227c478bd9Sstevel@tonic-gate /* 231c1abfbcSRod Evans * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate #include <sys/types.h> 277c478bd9Sstevel@tonic-gate #include <sys/mman.h> 287c478bd9Sstevel@tonic-gate #include <dirent.h> 297c478bd9Sstevel@tonic-gate #include <stdio.h> 308521e5e6Srie #include <stdlib.h> 317c478bd9Sstevel@tonic-gate #include <string.h> 327c478bd9Sstevel@tonic-gate #include <limits.h> 335aefb655Srie #include <debug.h> 345aefb655Srie #include <conv.h> 3508278a5eSRod Evans #include <elfcap.h> 367c478bd9Sstevel@tonic-gate #include "_rtld.h" 3708278a5eSRod Evans #include "_elf.h" 387c478bd9Sstevel@tonic-gate #include "_audit.h" 397c478bd9Sstevel@tonic-gate #include "msg.h" 407c478bd9Sstevel@tonic-gate 417c478bd9Sstevel@tonic-gate /* 4208278a5eSRod Evans * qsort(3c) capability comparison function. 437c478bd9Sstevel@tonic-gate */ 447c478bd9Sstevel@tonic-gate static int 451c1abfbcSRod Evans compare(const void *vp_a, const void *vp_b) 467c478bd9Sstevel@tonic-gate { 471c1abfbcSRod Evans Fdesc *fdp_a = (Fdesc *)vp_a, *fdp_b = (Fdesc *)vp_b; 4808278a5eSRod Evans char *strcap_a, *strcap_b; 4908278a5eSRod Evans Xword hwcap_a, hwcap_b; 507c478bd9Sstevel@tonic-gate 5108278a5eSRod Evans /* 5208278a5eSRod Evans * First, investigate any platform capability. 5308278a5eSRod Evans */ 541c1abfbcSRod Evans strcap_a = fdp_a->fd_scapset.sc_plat; 551c1abfbcSRod Evans strcap_b = fdp_b->fd_scapset.sc_plat; 567c478bd9Sstevel@tonic-gate 5708278a5eSRod Evans if (strcap_a && (strcap_b == NULL)) 587c478bd9Sstevel@tonic-gate return (-1); 5908278a5eSRod Evans if (strcap_b && (strcap_a == NULL)) 607c478bd9Sstevel@tonic-gate return (1); 6108278a5eSRod Evans 6208278a5eSRod Evans /* 6308278a5eSRod Evans * Second, investigate any machine capability. 6408278a5eSRod Evans */ 651c1abfbcSRod Evans strcap_a = fdp_a->fd_scapset.sc_mach; 661c1abfbcSRod Evans strcap_b = fdp_b->fd_scapset.sc_mach; 6708278a5eSRod Evans 6808278a5eSRod Evans if (strcap_a && (strcap_b == NULL)) 6908278a5eSRod Evans return (-1); 7008278a5eSRod Evans if (strcap_b && (strcap_a == NULL)) 7108278a5eSRod Evans return (1); 7208278a5eSRod Evans 7308278a5eSRod Evans /* 7408278a5eSRod Evans * Third, investigate any CA_SUNW_HW_2 hardware capabilities. 7508278a5eSRod Evans */ 761c1abfbcSRod Evans hwcap_a = fdp_a->fd_scapset.sc_hw_2; 771c1abfbcSRod Evans hwcap_b = fdp_b->fd_scapset.sc_hw_2; 7808278a5eSRod Evans 7908278a5eSRod Evans if (hwcap_a > hwcap_b) 8008278a5eSRod Evans return (-1); 8108278a5eSRod Evans if (hwcap_a < hwcap_b) 8208278a5eSRod Evans return (1); 8308278a5eSRod Evans 8408278a5eSRod Evans /* 8508278a5eSRod Evans * Finally, investigate any CA_SUNW_HW_1 hardware capabilities. 8608278a5eSRod Evans */ 871c1abfbcSRod Evans hwcap_a = fdp_a->fd_scapset.sc_hw_1; 881c1abfbcSRod Evans hwcap_b = fdp_b->fd_scapset.sc_hw_1; 8908278a5eSRod Evans 9008278a5eSRod Evans if (hwcap_a > hwcap_b) 9108278a5eSRod Evans return (-1); 9208278a5eSRod Evans if (hwcap_a < hwcap_b) 9308278a5eSRod Evans return (1); 9408278a5eSRod Evans 951c1abfbcSRod Evans /* 961c1abfbcSRod Evans * Normally, a capabilities directory contains one or more capabilities 971c1abfbcSRod Evans * files, each with different capabilities. The role of ld.so.1 is to 981c1abfbcSRod Evans * select the best candidate from these variants. However, we've come 991c1abfbcSRod Evans * across cases where files containing the same capabilities have been 1001c1abfbcSRod Evans * placed in the same capabilities directory. As we can't tell which 1011c1abfbcSRod Evans * file is the best, we select neither, and diagnose this suspicious 1021c1abfbcSRod Evans * scenario. 1031c1abfbcSRod Evans */ 1041c1abfbcSRod Evans DBG_CALL(Dbg_cap_identical(fdp_a->fd_lml, fdp_a->fd_nname, 1051c1abfbcSRod Evans fdp_b->fd_nname)); 1061c1abfbcSRod Evans 1071c1abfbcSRod Evans fdp_a->fd_flags |= FLG_FD_IGNORE; 1081c1abfbcSRod Evans fdp_b->fd_flags |= FLG_FD_IGNORE; 1091c1abfbcSRod Evans 1107c478bd9Sstevel@tonic-gate return (0); 1117c478bd9Sstevel@tonic-gate } 1127c478bd9Sstevel@tonic-gate 1137c478bd9Sstevel@tonic-gate /* 11408278a5eSRod Evans * Determine whether HWCAP1 capabilities value is supported. 1157c478bd9Sstevel@tonic-gate */ 1167c478bd9Sstevel@tonic-gate int 11708278a5eSRod Evans hwcap1_check(Syscapset *scapset, Xword val, Rej_desc *rej) 1187c478bd9Sstevel@tonic-gate { 11956deab07SRod Evans Xword mval; 1207c478bd9Sstevel@tonic-gate 121bebb829dSRod Evans /* 12256deab07SRod Evans * Ensure that the kernel can cope with the required capabilities. 123bebb829dSRod Evans */ 12408278a5eSRod Evans if ((rtld_flags2 & RT_FL2_HWCAP) && 12508278a5eSRod Evans ((mval = (val & ~scapset->sc_hw_1)) != 0)) { 12608278a5eSRod Evans if (rej) { 127de777a60Sab196087 static Conv_cap_val_hw1_buf_t cap_buf; 128de777a60Sab196087 1297c478bd9Sstevel@tonic-gate rej->rej_type = SGS_REJ_HWCAP_1; 13008278a5eSRod Evans rej->rej_str = conv_cap_val_hw1(mval, 13108278a5eSRod Evans M_MACH, 0, &cap_buf); 13208278a5eSRod Evans } 13308278a5eSRod Evans return (0); 13408278a5eSRod Evans } 13508278a5eSRod Evans return (1); 13608278a5eSRod Evans } 13708278a5eSRod Evans 13808278a5eSRod Evans /* 13908278a5eSRod Evans * Determine whether HWCAP2 capabilities value is supported. 14008278a5eSRod Evans */ 14108278a5eSRod Evans int 14208278a5eSRod Evans hwcap2_check(Syscapset *scapset, Xword val, Rej_desc *rej) 14308278a5eSRod Evans { 14408278a5eSRod Evans Xword mval; 14508278a5eSRod Evans 14608278a5eSRod Evans /* 14708278a5eSRod Evans * Ensure that the kernel can cope with the required capabilities. 14808278a5eSRod Evans */ 14908278a5eSRod Evans if ((mval = (val & ~scapset->sc_hw_2)) != 0) { 15008278a5eSRod Evans if (rej) { 15108278a5eSRod Evans static Conv_cap_val_hw2_buf_t cap_buf; 15208278a5eSRod Evans 15308278a5eSRod Evans rej->rej_type = SGS_REJ_HWCAP_2; 15408278a5eSRod Evans rej->rej_str = conv_cap_val_hw2(mval, 15508278a5eSRod Evans M_MACH, 0, &cap_buf); 15608278a5eSRod Evans } 1577c478bd9Sstevel@tonic-gate return (0); 1587c478bd9Sstevel@tonic-gate } 1597c478bd9Sstevel@tonic-gate return (1); 1607c478bd9Sstevel@tonic-gate } 1617c478bd9Sstevel@tonic-gate 1627c478bd9Sstevel@tonic-gate /* 16356deab07SRod Evans * Process any software capabilities. 1647c478bd9Sstevel@tonic-gate */ 16556deab07SRod Evans /* ARGSUSED0 */ 16656deab07SRod Evans int 16708278a5eSRod Evans sfcap1_check(Syscapset *scapset, Xword val, Rej_desc *rej) 16856deab07SRod Evans { 16956deab07SRod Evans #if defined(_ELF64) 17056deab07SRod Evans /* 17156deab07SRod Evans * A 64-bit executable that started the process can be restricted to a 17256deab07SRod Evans * 32-bit address space. A 64-bit dependency that is restricted to a 17356deab07SRod Evans * 32-bit address space can not be loaded unless the executable has 17456deab07SRod Evans * established this requirement. 17556deab07SRod Evans */ 17656deab07SRod Evans if ((val & SF1_SUNW_ADDR32) && ((rtld_flags2 & RT_FL2_ADDR32) == 0)) { 17708278a5eSRod Evans if (rej) { 17856deab07SRod Evans static Conv_cap_val_sf1_buf_t cap_buf; 1794464de07SAli Bahrami 18056deab07SRod Evans rej->rej_type = SGS_REJ_SFCAP_1; 18108278a5eSRod Evans rej->rej_str = conv_cap_val_sf1(SF1_SUNW_ADDR32, 18208278a5eSRod Evans M_MACH, 0, &cap_buf); 18308278a5eSRod Evans } 18456deab07SRod Evans return (0); 1857c478bd9Sstevel@tonic-gate } 18656deab07SRod Evans #endif 18756deab07SRod Evans return (1); 1884464de07SAli Bahrami } 1897c478bd9Sstevel@tonic-gate 1907c478bd9Sstevel@tonic-gate /* 19108278a5eSRod Evans * Process any platform capability. 19208278a5eSRod Evans */ 19308278a5eSRod Evans int 19408278a5eSRod Evans platcap_check(Syscapset *scapset, const char *str, Rej_desc *rej) 19508278a5eSRod Evans { 19608278a5eSRod Evans /* 19708278a5eSRod Evans * If the platform name hasn't been set, try and obtain it. 19808278a5eSRod Evans */ 19908278a5eSRod Evans if ((scapset->sc_plat == NULL) && 20008278a5eSRod Evans (scapset->sc_platsz == 0)) 20108278a5eSRod Evans platform_name(scapset); 20208278a5eSRod Evans 20308278a5eSRod Evans if ((scapset->sc_plat == NULL) || 20408278a5eSRod Evans (str && strcmp(scapset->sc_plat, str))) { 20508278a5eSRod Evans if (rej) { 20608278a5eSRod Evans /* 20708278a5eSRod Evans * Note, the platform name points to a string within an 20808278a5eSRod Evans * objects string table, and if that object can't be 20908278a5eSRod Evans * loaded, it will be unloaded and thus invalidate the 21008278a5eSRod Evans * string. Duplicate the string here for rejection 21108278a5eSRod Evans * message inheritance. 21208278a5eSRod Evans */ 21308278a5eSRod Evans rej->rej_type = SGS_REJ_PLATCAP; 21408278a5eSRod Evans rej->rej_str = stravl_insert(str, 0, 0, 0); 21508278a5eSRod Evans } 21608278a5eSRod Evans return (0); 21708278a5eSRod Evans } 21808278a5eSRod Evans return (1); 21908278a5eSRod Evans } 22008278a5eSRod Evans 22108278a5eSRod Evans /* 22208278a5eSRod Evans * Process any machine capability. 22308278a5eSRod Evans */ 22408278a5eSRod Evans int 22508278a5eSRod Evans machcap_check(Syscapset *scapset, const char *str, Rej_desc *rej) 22608278a5eSRod Evans { 22708278a5eSRod Evans /* 22808278a5eSRod Evans * If the machine name hasn't been set, try and obtain it. 22908278a5eSRod Evans */ 23008278a5eSRod Evans if ((scapset->sc_mach == NULL) && 23108278a5eSRod Evans (scapset->sc_machsz == 0)) 23208278a5eSRod Evans machine_name(scapset); 23308278a5eSRod Evans 23408278a5eSRod Evans if ((scapset->sc_mach == NULL) || 23508278a5eSRod Evans (str && strcmp(scapset->sc_mach, str))) { 23608278a5eSRod Evans if (rej) { 23708278a5eSRod Evans /* 23808278a5eSRod Evans * Note, the machine name points to a string within an 23908278a5eSRod Evans * objects string table, and if that object can't be 24008278a5eSRod Evans * loaded, it will be unloaded and thus invalidate the 24108278a5eSRod Evans * string. Duplicate the string here for rejection 24208278a5eSRod Evans * message inheritance. 24308278a5eSRod Evans */ 24408278a5eSRod Evans rej->rej_type = SGS_REJ_MACHCAP; 24508278a5eSRod Evans rej->rej_str = stravl_insert(str, 0, 0, 0); 24608278a5eSRod Evans } 24708278a5eSRod Evans return (0); 24808278a5eSRod Evans } 24908278a5eSRod Evans return (1); 25008278a5eSRod Evans } 25108278a5eSRod Evans 25208278a5eSRod Evans /* 25308278a5eSRod Evans * Generic front-end to capabilities validation. 2547c478bd9Sstevel@tonic-gate */ 255cce0e03bSab196087 static int 25608278a5eSRod Evans cap_check(Cap *cptr, char *strs, int alt, Fdesc *fdp, Rej_desc *rej) 25708278a5eSRod Evans { 25808278a5eSRod Evans Syscapset *scapset; 25908278a5eSRod Evans int totplat, ivlplat, totmach, ivlmach; 26008278a5eSRod Evans 26108278a5eSRod Evans /* 26208278a5eSRod Evans * If the caller has no capabilities, then the object is valid. 26308278a5eSRod Evans */ 26408278a5eSRod Evans if (cptr == NULL) 26508278a5eSRod Evans return (1); 26608278a5eSRod Evans 26708278a5eSRod Evans if (alt) 26808278a5eSRod Evans scapset = alt_scapset; 26908278a5eSRod Evans else 27008278a5eSRod Evans scapset = org_scapset; 27108278a5eSRod Evans 27208278a5eSRod Evans totplat = ivlplat = totmach = ivlmach = 0; 27308278a5eSRod Evans 27408278a5eSRod Evans while (cptr->c_tag != CA_SUNW_NULL) { 27508278a5eSRod Evans Xword val = cptr->c_un.c_val; 27608278a5eSRod Evans char *str; 27708278a5eSRod Evans 27808278a5eSRod Evans switch (cptr->c_tag) { 27908278a5eSRod Evans case CA_SUNW_HW_1: 2804e12d685SRod Evans /* 2814e12d685SRod Evans * Remove any historic values that should not be 2824e12d685SRod Evans * involved with any validation. 2834e12d685SRod Evans */ 2844e12d685SRod Evans val &= ~AV_HW1_IGNORE; 2854e12d685SRod Evans 28608278a5eSRod Evans if (hwcap1_check(scapset, val, rej) == 0) 28708278a5eSRod Evans return (0); 28808278a5eSRod Evans if (fdp) 28908278a5eSRod Evans fdp->fd_scapset.sc_hw_1 = val; 29008278a5eSRod Evans break; 29108278a5eSRod Evans case CA_SUNW_SF_1: 29208278a5eSRod Evans if (sfcap1_check(scapset, val, rej) == 0) 29308278a5eSRod Evans return (0); 29408278a5eSRod Evans if (fdp) 29508278a5eSRod Evans fdp->fd_scapset.sc_sf_1 = val; 29608278a5eSRod Evans break; 29708278a5eSRod Evans case CA_SUNW_HW_2: 29808278a5eSRod Evans if (hwcap2_check(scapset, val, rej) == 0) 29908278a5eSRod Evans return (0); 30008278a5eSRod Evans if (fdp) 30108278a5eSRod Evans fdp->fd_scapset.sc_hw_2 = val; 30208278a5eSRod Evans break; 30308278a5eSRod Evans case CA_SUNW_PLAT: 30408278a5eSRod Evans /* 30508278a5eSRod Evans * A capabilities group can define multiple platform 30608278a5eSRod Evans * names that are appropriate. Only if all the names 30708278a5eSRod Evans * are deemed invalid is the group determined 30808278a5eSRod Evans * inappropriate. 30908278a5eSRod Evans */ 31008278a5eSRod Evans if (totplat == ivlplat) { 31108278a5eSRod Evans totplat++; 31208278a5eSRod Evans 31308278a5eSRod Evans str = strs + val; 31408278a5eSRod Evans 31508278a5eSRod Evans if (platcap_check(scapset, str, rej) == 0) 31608278a5eSRod Evans ivlplat++; 31708278a5eSRod Evans else if (fdp) 31808278a5eSRod Evans fdp->fd_scapset.sc_plat = str; 31908278a5eSRod Evans } 32008278a5eSRod Evans break; 32108278a5eSRod Evans case CA_SUNW_MACH: 32208278a5eSRod Evans /* 32308278a5eSRod Evans * A capabilities group can define multiple machine 32408278a5eSRod Evans * names that are appropriate. Only if all the names 32508278a5eSRod Evans * are deemed invalid is the group determined 32608278a5eSRod Evans * inappropriate. 32708278a5eSRod Evans */ 32808278a5eSRod Evans if (totmach == ivlmach) { 32908278a5eSRod Evans totmach++; 33008278a5eSRod Evans 33108278a5eSRod Evans str = strs + val; 33208278a5eSRod Evans 33308278a5eSRod Evans if (machcap_check(scapset, str, rej) == 0) 33408278a5eSRod Evans ivlmach++; 33508278a5eSRod Evans else if (fdp) 33608278a5eSRod Evans fdp->fd_scapset.sc_mach = str; 33708278a5eSRod Evans } 33808278a5eSRod Evans break; 33908278a5eSRod Evans case CA_SUNW_ID: 34008278a5eSRod Evans /* 34108278a5eSRod Evans * Capabilities identifiers provide for diagnostics, 34208278a5eSRod Evans * but are not attributes that must be compared with 34308278a5eSRod Evans * the system. They are ignored. 34408278a5eSRod Evans */ 34508278a5eSRod Evans break; 34608278a5eSRod Evans default: 34708278a5eSRod Evans rej->rej_type = SGS_REJ_UNKCAP; 34808278a5eSRod Evans rej->rej_info = cptr->c_tag; 34908278a5eSRod Evans return (0); 35008278a5eSRod Evans } 35108278a5eSRod Evans cptr++; 35208278a5eSRod Evans } 35308278a5eSRod Evans 35408278a5eSRod Evans /* 35508278a5eSRod Evans * If any platform names, or machine names were found, and all were 35608278a5eSRod Evans * invalid, indicate that the object is inappropriate. 35708278a5eSRod Evans */ 35808278a5eSRod Evans if ((totplat && (totplat == ivlplat)) || 35908278a5eSRod Evans (totmach && (totmach == ivlmach))) 36008278a5eSRod Evans return (0); 36108278a5eSRod Evans 36208278a5eSRod Evans return (1); 36308278a5eSRod Evans } 36408278a5eSRod Evans 36508278a5eSRod Evans #define HWAVL_RECORDED(n) pnavl_recorded(&capavl, n, NULL, NULL) 36608278a5eSRod Evans 36708278a5eSRod Evans /* 36808278a5eSRod Evans * Determine whether a link-map should use alternative system capabilities. 36908278a5eSRod Evans */ 37008278a5eSRod Evans static void 37108278a5eSRod Evans cap_check_lmp_init(Rt_map *lmp) 37208278a5eSRod Evans { 37308278a5eSRod Evans int alt = 0; 37408278a5eSRod Evans 37508278a5eSRod Evans /* 37608278a5eSRod Evans * If an alternative set of system capabilities have been established, 37708278a5eSRod Evans * and only specific files should use these alternative system 37808278a5eSRod Evans * capabilities, determine whether this file is one of those specified. 37908278a5eSRod Evans */ 38008278a5eSRod Evans if (capavl) { 38108278a5eSRod Evans const char *file; 38208278a5eSRod Evans 38308278a5eSRod Evans /* 38408278a5eSRod Evans * The simplest way to reference a file is to use its file name 38508278a5eSRod Evans * (soname), however try all of the names that this file is 38608278a5eSRod Evans * known by. 38708278a5eSRod Evans */ 38808278a5eSRod Evans if ((file = strrchr(NAME(lmp), '/')) != NULL) 38908278a5eSRod Evans file++; 39008278a5eSRod Evans else 39108278a5eSRod Evans file = NULL; 39208278a5eSRod Evans 39308278a5eSRod Evans if ((file && (HWAVL_RECORDED(file) != 0)) || 39408278a5eSRod Evans (HWAVL_RECORDED(NAME(lmp)) != 0) || 39508278a5eSRod Evans ((PATHNAME(lmp) != NAME(lmp)) && 39608278a5eSRod Evans (HWAVL_RECORDED(PATHNAME(lmp)) != 0))) 39708278a5eSRod Evans alt = 1; 39808278a5eSRod Evans 39908278a5eSRod Evans if (alt == 0) { 40008278a5eSRod Evans Aliste idx; 40108278a5eSRod Evans const char *cp; 40208278a5eSRod Evans 40308278a5eSRod Evans for (APLIST_TRAVERSE(ALIAS(lmp), idx, cp)) { 40408278a5eSRod Evans if ((alt = HWAVL_RECORDED(cp)) != 0) 40508278a5eSRod Evans break; 40608278a5eSRod Evans } 40708278a5eSRod Evans } 40808278a5eSRod Evans } 40908278a5eSRod Evans 41008278a5eSRod Evans /* 41108278a5eSRod Evans * Indicate if this link-map should use alternative system capabilities, 41208278a5eSRod Evans * and that the alternative system capabilities check has been carried 41308278a5eSRod Evans * out. 41408278a5eSRod Evans */ 41508278a5eSRod Evans if ((org_scapset != alt_scapset) && ((capavl == NULL) || alt)) 41608278a5eSRod Evans FLAGS1(lmp) |= FL1_RT_ALTCAP; 41708278a5eSRod Evans FLAGS1(lmp) |= FL1_RT_ALTCHECK; 41808278a5eSRod Evans } 41908278a5eSRod Evans 42008278a5eSRod Evans /* 42108278a5eSRod Evans * Validate the capabilities requirements of a link-map. 42208278a5eSRod Evans * 42308278a5eSRod Evans * This routine is called for main, where a link-map is constructed from the 42408278a5eSRod Evans * mappings returned from exec(), and for any symbol capabilities comparisons. 42508278a5eSRod Evans */ 42608278a5eSRod Evans int 42708278a5eSRod Evans cap_check_lmp(Rt_map *lmp, Rej_desc *rej) 42808278a5eSRod Evans { 42908278a5eSRod Evans if ((FLAGS1(lmp) & FL1_RT_ALTCHECK) == 0) 43008278a5eSRod Evans cap_check_lmp_init(lmp); 43108278a5eSRod Evans 43208278a5eSRod Evans return (cap_check(CAP(lmp), STRTAB(lmp), 43308278a5eSRod Evans (FLAGS1(lmp) & FL1_RT_ALTCAP), NULL, rej)); 43408278a5eSRod Evans } 43508278a5eSRod Evans 43608278a5eSRod Evans /* 43708278a5eSRod Evans * Validate the capabilities requirements of a file under inspection. 43808278a5eSRod Evans * This file is still under the early stages of loading, and has no link-map 43908278a5eSRod Evans * yet. The file must have an object capabilities definition (PT_SUNWCAP), to 44008278a5eSRod Evans * have gotten us here. The logic here is the same as cap_check_lmp(). 44108278a5eSRod Evans */ 44208278a5eSRod Evans int 44308278a5eSRod Evans cap_check_fdesc(Fdesc *fdp, Cap *cptr, char *strs, Rej_desc *rej) 44408278a5eSRod Evans { 44508278a5eSRod Evans int alt = 0; 44608278a5eSRod Evans 44708278a5eSRod Evans /* 44808278a5eSRod Evans * If an alternative set of system capabilities have been established, 44908278a5eSRod Evans * and only specific files should use these alternative system 45008278a5eSRod Evans * capabilities, determine whether this file is one of those specified. 45108278a5eSRod Evans */ 45208278a5eSRod Evans if (capavl) { 45308278a5eSRod Evans const char *file; 45408278a5eSRod Evans 45508278a5eSRod Evans /* 45608278a5eSRod Evans * The simplest way to reference a file is to use its file name 45708278a5eSRod Evans * (soname), however try all of the names that this file is 45808278a5eSRod Evans * known by. 45908278a5eSRod Evans */ 4601c1abfbcSRod Evans if (fdp->fd_oname && 4611c1abfbcSRod Evans ((file = strrchr(fdp->fd_oname, '/')) != NULL)) 46208278a5eSRod Evans file++; 46308278a5eSRod Evans else 46408278a5eSRod Evans file = NULL; 46508278a5eSRod Evans 46608278a5eSRod Evans if ((file && (HWAVL_RECORDED(file) != 0)) || 46708278a5eSRod Evans (fdp->fd_oname && (HWAVL_RECORDED(fdp->fd_oname) != 0)) || 46808278a5eSRod Evans (fdp->fd_nname && (HWAVL_RECORDED(fdp->fd_nname) != 0)) || 46908278a5eSRod Evans (fdp->fd_pname && (fdp->fd_pname != fdp->fd_nname) && 47008278a5eSRod Evans (HWAVL_RECORDED(fdp->fd_pname) != 0))) 47108278a5eSRod Evans alt = 1; 47208278a5eSRod Evans } 47308278a5eSRod Evans 47408278a5eSRod Evans /* 47508278a5eSRod Evans * Indicate if this file descriptor should use alternative system 47608278a5eSRod Evans * capabilities, and that the alternative system capabilities check has 47708278a5eSRod Evans * been carried out. 47808278a5eSRod Evans */ 47908278a5eSRod Evans if ((org_scapset != alt_scapset) && ((capavl == NULL) || alt)) 48008278a5eSRod Evans fdp->fd_flags |= FLG_FD_ALTCAP; 48108278a5eSRod Evans fdp->fd_flags |= FLG_FD_ALTCHECK; 48208278a5eSRod Evans 48308278a5eSRod Evans /* 48408278a5eSRod Evans * Verify that the required capabilities are supported by the reference. 48508278a5eSRod Evans */ 48608278a5eSRod Evans return (cap_check(cptr, strs, (fdp->fd_flags & FLG_FD_ALTCAP), 48708278a5eSRod Evans fdp, rej)); 48808278a5eSRod Evans } 48908278a5eSRod Evans 49008278a5eSRod Evans /* 49108278a5eSRod Evans * Free a file descriptor list. As part of building this list, the original 49208278a5eSRod Evans * names for each capabilities candidate were duplicated for use in later 49308278a5eSRod Evans * diagnostics. These names need to be freed. 49408278a5eSRod Evans */ 49508278a5eSRod Evans void 49608278a5eSRod Evans free_fd(Alist *fdalp) 49708278a5eSRod Evans { 49808278a5eSRod Evans if (fdalp) { 49908278a5eSRod Evans Aliste idx; 50008278a5eSRod Evans Fdesc *fdp; 50108278a5eSRod Evans 50208278a5eSRod Evans for (ALIST_TRAVERSE(fdalp, idx, fdp)) { 50308278a5eSRod Evans if (fdp->fd_oname) 50408278a5eSRod Evans free((void *)fdp->fd_oname); 50508278a5eSRod Evans } 50608278a5eSRod Evans free(fdalp); 50708278a5eSRod Evans } 50808278a5eSRod Evans } 50908278a5eSRod Evans 51008278a5eSRod Evans /* 51108278a5eSRod Evans * When $CAPABILITY (or $HWCAP) is used to represent dependencies, take the 51208278a5eSRod Evans * associated directory and analyze all the files it contains. 51308278a5eSRod Evans */ 51408278a5eSRod Evans static int 51508278a5eSRod Evans cap_dir(Alist **fdalpp, Lm_list *lml, const char *dname, Rt_map *clmp, 5169aa23310Srie uint_t flags, Rej_desc *rej, int *in_nfavl) 5177c478bd9Sstevel@tonic-gate { 5187c478bd9Sstevel@tonic-gate char path[PATH_MAX], *dst; 5197c478bd9Sstevel@tonic-gate const char *src; 5207c478bd9Sstevel@tonic-gate DIR *dir; 5217c478bd9Sstevel@tonic-gate struct dirent *dirent; 522cce0e03bSab196087 Alist *fdalp = NULL; 5231c1abfbcSRod Evans Aliste idx; 5241c1abfbcSRod Evans Fdesc *fdp; 5257c478bd9Sstevel@tonic-gate int error = 0; 5267c478bd9Sstevel@tonic-gate 5277c478bd9Sstevel@tonic-gate /* 5287c478bd9Sstevel@tonic-gate * Access the directory in preparation for reading its entries. If 5297c478bd9Sstevel@tonic-gate * successful, establish the initial pathname. 5307c478bd9Sstevel@tonic-gate */ 53156deab07SRod Evans if ((dir = opendir(dname)) == NULL) { 5327c478bd9Sstevel@tonic-gate Rej_desc _rej = { 0 }; 5337c478bd9Sstevel@tonic-gate 5347c478bd9Sstevel@tonic-gate _rej.rej_type = SGS_REJ_STR; 53556deab07SRod Evans _rej.rej_name = dname; 5367c478bd9Sstevel@tonic-gate _rej.rej_str = strerror(errno); 537ba2be530Sab196087 DBG_CALL(Dbg_file_rejected(lml, &_rej, M_MACH)); 53831fdd7caSab196087 rejection_inherit(rej, &_rej); 5397c478bd9Sstevel@tonic-gate return (0); 5407c478bd9Sstevel@tonic-gate } 5417c478bd9Sstevel@tonic-gate 54256deab07SRod Evans for (dst = path, src = dname; *src; dst++, src++) 5437c478bd9Sstevel@tonic-gate *dst = *src; 5447c478bd9Sstevel@tonic-gate *dst++ = '/'; 5457c478bd9Sstevel@tonic-gate 5467c478bd9Sstevel@tonic-gate /* 5477c478bd9Sstevel@tonic-gate * Read each entry from the directory and determine whether it is a 5487c478bd9Sstevel@tonic-gate * valid ELF file. 5497c478bd9Sstevel@tonic-gate */ 5507c478bd9Sstevel@tonic-gate while ((dirent = readdir(dir)) != NULL) { 55156deab07SRod Evans const char *file = dirent->d_name; 5527247f888Srie char *_dst; 55356deab07SRod Evans Fdesc fd = { 0 }; 5547c478bd9Sstevel@tonic-gate Rej_desc _rej = { 0 }; 55556deab07SRod Evans Pdesc pd = { 0 }; 5567c478bd9Sstevel@tonic-gate 5577c478bd9Sstevel@tonic-gate /* 5587c478bd9Sstevel@tonic-gate * Ignore "." and ".." entries. 5597c478bd9Sstevel@tonic-gate */ 5607c478bd9Sstevel@tonic-gate if ((file[0] == '.') && ((file[1] == '\0') || 5617c478bd9Sstevel@tonic-gate ((file[1] == '.') && (file[2] == '\0')))) 5627c478bd9Sstevel@tonic-gate continue; 5637c478bd9Sstevel@tonic-gate 5647c478bd9Sstevel@tonic-gate /* 56556deab07SRod Evans * Complete the full pathname. 5667c478bd9Sstevel@tonic-gate */ 5677c478bd9Sstevel@tonic-gate for (_dst = dst, src = file, file = dst; *src; _dst++, src++) 5687c478bd9Sstevel@tonic-gate *_dst = *src; 5697c478bd9Sstevel@tonic-gate *_dst = '\0'; 5707c478bd9Sstevel@tonic-gate 57156deab07SRod Evans /* 57256deab07SRod Evans * Trace the inspection of this file, and determine any 57356deab07SRod Evans * auditor substitution. 57456deab07SRod Evans */ 57556deab07SRod Evans pd.pd_pname = path; 57656deab07SRod Evans pd.pd_flags = PD_FLG_PNSLASH; 5777c478bd9Sstevel@tonic-gate 57856deab07SRod Evans if (load_trace(lml, &pd, clmp, &fd) == NULL) 5798521e5e6Srie continue; 5808521e5e6Srie 5818521e5e6Srie /* 5828521e5e6Srie * Note, all directory entries are processed by find_path(), 5838521e5e6Srie * even entries that are directories themselves. This single 5848521e5e6Srie * point for control keeps the number of stat()'s down, and 5858521e5e6Srie * provides a single point for error diagnostics. 5868521e5e6Srie */ 58756deab07SRod Evans if (find_path(lml, clmp, flags, &fd, &_rej, in_nfavl) == 0) { 58831fdd7caSab196087 rejection_inherit(rej, &_rej); 5898521e5e6Srie continue; 5908521e5e6Srie } 5918521e5e6Srie 59208278a5eSRod Evans DBG_CALL(Dbg_cap_candidate(lml, fd.fd_nname)); 5937c478bd9Sstevel@tonic-gate 5947c478bd9Sstevel@tonic-gate /* 59508278a5eSRod Evans * If this object has already been loaded, save the capabilities 59608278a5eSRod Evans * for later sorting. Otherwise we have a new candidate. 5977c478bd9Sstevel@tonic-gate */ 59856deab07SRod Evans if (fd.fd_lmp) 59908278a5eSRod Evans fd.fd_scapset = CAPSET(fd.fd_lmp); 6001c1abfbcSRod Evans fd.fd_lml = lml; 601c174926fSrie 60208278a5eSRod Evans /* 60308278a5eSRod Evans * Duplicate the original name, as this may be required for 60408278a5eSRod Evans * later diagnostics. Keep a copy of the file descriptor for 60508278a5eSRod Evans * analysis once all capabilities candidates have been 60608278a5eSRod Evans * determined. 60708278a5eSRod Evans */ 60808278a5eSRod Evans if (((fd.fd_oname = strdup(fd.fd_oname)) == NULL) || 60908278a5eSRod Evans (alist_append(&fdalp, &fd, sizeof (Fdesc), 61008278a5eSRod Evans AL_CNT_CAP) == NULL)) { 6117c478bd9Sstevel@tonic-gate error = 1; 6127c478bd9Sstevel@tonic-gate break; 6137c478bd9Sstevel@tonic-gate } 6147c478bd9Sstevel@tonic-gate } 6157c478bd9Sstevel@tonic-gate (void) closedir(dir); 6167c478bd9Sstevel@tonic-gate 6177c478bd9Sstevel@tonic-gate /* 6187c478bd9Sstevel@tonic-gate * If no objects have been found, we're done. Also, if an allocation 6197c478bd9Sstevel@tonic-gate * error occurred while processing any object, remove any objects that 6207c478bd9Sstevel@tonic-gate * had already been added to the list and return. 6217c478bd9Sstevel@tonic-gate */ 622cce0e03bSab196087 if ((fdalp == NULL) || error) { 62356deab07SRod Evans if (fdalp) 62408278a5eSRod Evans free_fd(fdalp); 6257c478bd9Sstevel@tonic-gate return (0); 6267c478bd9Sstevel@tonic-gate } 6277c478bd9Sstevel@tonic-gate 6287c478bd9Sstevel@tonic-gate /* 6297c478bd9Sstevel@tonic-gate * Having processed and retained all candidates from this directory, 6307c478bd9Sstevel@tonic-gate * sort them, based on the precedence of their hardware capabilities. 6317c478bd9Sstevel@tonic-gate */ 632cce0e03bSab196087 qsort(fdalp->al_data, fdalp->al_nitems, fdalp->al_size, compare); 6337c478bd9Sstevel@tonic-gate 6341c1abfbcSRod Evans /* 6351c1abfbcSRod Evans * If any objects were found to have the same capabilities, then these 6361c1abfbcSRod Evans * objects must be rejected, as we can't tell which object is more 6371c1abfbcSRod Evans * appropriate. 6381c1abfbcSRod Evans */ 6391c1abfbcSRod Evans for (ALIST_TRAVERSE(fdalp, idx, fdp)) { 6401c1abfbcSRod Evans if (fdp->fd_flags & FLG_FD_IGNORE) 6411c1abfbcSRod Evans alist_delete(fdalp, &idx); 6421c1abfbcSRod Evans } 6431c1abfbcSRod Evans 6441c1abfbcSRod Evans if (fdalp->al_nitems == 0) { 6451c1abfbcSRod Evans free_fd(fdalp); 6461c1abfbcSRod Evans return (0); 6471c1abfbcSRod Evans } 6481c1abfbcSRod Evans 6497c478bd9Sstevel@tonic-gate *fdalpp = fdalp; 6507c478bd9Sstevel@tonic-gate return (1); 6517c478bd9Sstevel@tonic-gate } 6527c478bd9Sstevel@tonic-gate 65356deab07SRod Evans int 65408278a5eSRod Evans cap_filtees(Alist **alpp, Aliste oidx, const char *dir, Aliste nlmco, 6552020b2b6SRod Evans Rt_map *flmp, Rt_map *clmp, const char *ref, int mode, uint_t flags, 6562020b2b6SRod Evans int *in_nfavl) 6577c478bd9Sstevel@tonic-gate { 658cce0e03bSab196087 Alist *fdalp = NULL; 659cce0e03bSab196087 Aliste idx; 6607c478bd9Sstevel@tonic-gate Fdesc *fdp; 6617c478bd9Sstevel@tonic-gate Lm_list *lml = LIST(flmp); 6627c478bd9Sstevel@tonic-gate int unused = 0; 6637c478bd9Sstevel@tonic-gate Rej_desc rej = { 0 }; 6647c478bd9Sstevel@tonic-gate 66508278a5eSRod Evans if (cap_dir(&fdalp, lml, dir, flmp, flags, &rej, in_nfavl) == 0) 6667c478bd9Sstevel@tonic-gate return (0); 6677c478bd9Sstevel@tonic-gate 6687c478bd9Sstevel@tonic-gate /* 6697c478bd9Sstevel@tonic-gate * Now complete the mapping of each of the ordered objects, adding 67056deab07SRod Evans * each object to a new pathname descriptor. 6717c478bd9Sstevel@tonic-gate */ 672cce0e03bSab196087 for (ALIST_TRAVERSE(fdalp, idx, fdp)) { 6737c478bd9Sstevel@tonic-gate Rt_map *nlmp; 674481bba9eSRod Evans Grp_hdl *ghp = NULL; 67556deab07SRod Evans Pdesc *pdp; 676fb1354edSrie int audit = 0; 6777c478bd9Sstevel@tonic-gate 67856deab07SRod Evans if (unused) 6797c478bd9Sstevel@tonic-gate continue; 6807c478bd9Sstevel@tonic-gate 6817c478bd9Sstevel@tonic-gate /* 6827c478bd9Sstevel@tonic-gate * Complete mapping the file, obtaining a handle, and continue 6837c478bd9Sstevel@tonic-gate * to analyze the object, establishing dependencies and 6847c478bd9Sstevel@tonic-gate * relocating. Remove the file descriptor at this point, as it 6857c478bd9Sstevel@tonic-gate * is no longer required. 6867c478bd9Sstevel@tonic-gate */ 6875aefb655Srie DBG_CALL(Dbg_file_filtee(lml, NAME(flmp), fdp->fd_nname, 0)); 6887c478bd9Sstevel@tonic-gate 68956deab07SRod Evans nlmp = load_path(lml, nlmco, flmp, mode, 6902017c965SRod Evans (flags | FLG_RT_PUBHDL), &ghp, fdp, &rej, in_nfavl); 691481bba9eSRod Evans if (nlmp == NULL) 69202ca3e02Srie continue; 6937c478bd9Sstevel@tonic-gate 6947c478bd9Sstevel@tonic-gate /* 69556deab07SRod Evans * Create a new pathname descriptor to represent this filtee, 69656deab07SRod Evans * and insert this descriptor in the Alist following the 69756deab07SRod Evans * hardware descriptor that seeded this processing. 6987c478bd9Sstevel@tonic-gate */ 69956deab07SRod Evans if ((pdp = alist_insert(alpp, 0, sizeof (Pdesc), 70056deab07SRod Evans AL_CNT_FILTEES, ++oidx)) == NULL) { 70156deab07SRod Evans if (ghp) 702481bba9eSRod Evans remove_lmc(lml, flmp, nlmco, NAME(nlmp)); 703fb1354edSrie return (0); 7047c478bd9Sstevel@tonic-gate } 7057c478bd9Sstevel@tonic-gate 70656deab07SRod Evans pdp->pd_pname = NAME(nlmp); 70756deab07SRod Evans pdp->pd_plen = strlen(NAME(nlmp)); 7087c478bd9Sstevel@tonic-gate 7097c478bd9Sstevel@tonic-gate /* 710fb1354edSrie * Establish the filter handle to prevent any recursion. 711fb1354edSrie */ 712fb1354edSrie if (nlmp && ghp) { 713fb1354edSrie ghp->gh_flags |= GPH_FILTEE; 71456deab07SRod Evans pdp->pd_info = (void *)ghp; 715fb1354edSrie } 716fb1354edSrie 717fb1354edSrie /* 718fb1354edSrie * Audit the filter/filtee established. A return of 0 719fb1354edSrie * indicates the auditor wishes to ignore this filtee. 720fb1354edSrie */ 721fb1354edSrie if (nlmp && (lml->lm_tflags | FLAGS1(flmp)) & 722fb1354edSrie LML_TFLG_AUD_OBJFILTER) { 723fb1354edSrie if (audit_objfilter(flmp, ref, nlmp, 0) == 0) { 724fb1354edSrie audit = 1; 725481bba9eSRod Evans nlmp = NULL; 726fb1354edSrie } 727fb1354edSrie } 728fb1354edSrie 729fb1354edSrie /* 730fb1354edSrie * Finish processing the objects associated with this request. 731fb1354edSrie */ 73256deab07SRod Evans if (nlmp && ghp && (((nlmp = analyze_lmc(lml, nlmco, nlmp, 7332020b2b6SRod Evans clmp, in_nfavl)) == NULL) || 7349aa23310Srie (relocate_lmc(lml, nlmco, flmp, nlmp, in_nfavl) == 0))) 73556deab07SRod Evans nlmp = NULL; 736fb1354edSrie 737fb1354edSrie /* 73802ca3e02Srie * If the filtee has been successfully processed, then create 73902ca3e02Srie * an association between the filter and the filtee. This 740fb1354edSrie * association provides sufficient information to tear down the 741fb1354edSrie * filter and filtee if necessary. 742fb1354edSrie */ 7438af2c5b9Srie DBG_CALL(Dbg_file_hdl_title(DBG_HDL_ADD)); 7442017c965SRod Evans if (nlmp && ghp && 7452017c965SRod Evans (hdl_add(ghp, flmp, GPD_FILTER, NULL) == NULL)) 74656deab07SRod Evans nlmp = NULL; 747fb1354edSrie 748fb1354edSrie /* 7497c478bd9Sstevel@tonic-gate * If this object is marked an end-filtee, we're done. 7507c478bd9Sstevel@tonic-gate */ 751fb1354edSrie if (nlmp && ghp && (FLAGS1(nlmp) & FL1_RT_ENDFILTE)) 7527c478bd9Sstevel@tonic-gate unused = 1; 753fb1354edSrie 754fb1354edSrie /* 75502ca3e02Srie * If this filtee loading has failed, generate a diagnostic. 75656deab07SRod Evans * Null out the path name descriptor entry, and continue the 75756deab07SRod Evans * search. 758fb1354edSrie */ 75956deab07SRod Evans if (nlmp == NULL) { 76056deab07SRod Evans DBG_CALL(Dbg_file_filtee(lml, 0, pdp->pd_pname, audit)); 76156deab07SRod Evans 76202ca3e02Srie /* 76302ca3e02Srie * If attempting to load this filtee required a new 76402ca3e02Srie * link-map control list to which this request has 76502ca3e02Srie * added objects, then remove all the objects that 76602ca3e02Srie * have been associated to this request. 76702ca3e02Srie */ 768481bba9eSRod Evans if (nlmco != ALIST_OFF_DATA) 769481bba9eSRod Evans remove_lmc(lml, flmp, nlmco, pdp->pd_pname); 77002ca3e02Srie 77156deab07SRod Evans pdp->pd_plen = 0; 772481bba9eSRod Evans pdp->pd_info = NULL; 773fb1354edSrie } 7747c478bd9Sstevel@tonic-gate } 7757c478bd9Sstevel@tonic-gate 77608278a5eSRod Evans free_fd(fdalp); 77756deab07SRod Evans return (1); 7787c478bd9Sstevel@tonic-gate } 7797c478bd9Sstevel@tonic-gate 7807c478bd9Sstevel@tonic-gate /* 78108278a5eSRod Evans * Load an individual capabilities object. 7827c478bd9Sstevel@tonic-gate */ 7837c478bd9Sstevel@tonic-gate Rt_map * 78408278a5eSRod Evans load_cap(Lm_list *lml, Aliste lmco, const char *dir, Rt_map *clmp, 7859aa23310Srie uint_t mode, uint_t flags, Grp_hdl **hdl, Rej_desc *rej, int *in_nfavl) 7867c478bd9Sstevel@tonic-gate { 787cce0e03bSab196087 Alist *fdalp = NULL; 788cce0e03bSab196087 Aliste idx; 7897c478bd9Sstevel@tonic-gate Fdesc *fdp; 7907c478bd9Sstevel@tonic-gate int found = 0; 791481bba9eSRod Evans Rt_map *lmp = NULL; 7927c478bd9Sstevel@tonic-gate 7937c478bd9Sstevel@tonic-gate /* 79456deab07SRod Evans * Obtain the sorted list of hardware capabilities objects available. 7957c478bd9Sstevel@tonic-gate */ 79608278a5eSRod Evans if (cap_dir(&fdalp, lml, dir, clmp, flags, rej, in_nfavl) == 0) 79756deab07SRod Evans return (NULL); 7987c478bd9Sstevel@tonic-gate 7997c478bd9Sstevel@tonic-gate /* 8007c478bd9Sstevel@tonic-gate * From the list of hardware capability objects, use the first and 8017c478bd9Sstevel@tonic-gate * discard the rest. 8027c478bd9Sstevel@tonic-gate */ 803cce0e03bSab196087 for (ALIST_TRAVERSE(fdalp, idx, fdp)) { 80456deab07SRod Evans Fdesc fd = *fdp; 8057c478bd9Sstevel@tonic-gate 80656deab07SRod Evans if ((found == 0) && ((lmp = load_path(lml, lmco, clmp, mode, 807481bba9eSRod Evans flags, hdl, &fd, rej, in_nfavl)) != NULL)) 80856deab07SRod Evans found++; 8097c478bd9Sstevel@tonic-gate } 8107c478bd9Sstevel@tonic-gate 81108278a5eSRod Evans free_fd(fdalp); 8127c478bd9Sstevel@tonic-gate return (lmp); 8137c478bd9Sstevel@tonic-gate } 81408278a5eSRod Evans 81508278a5eSRod Evans /* 81608278a5eSRod Evans * Use a case insensitive string match when looking up capability mask 81708278a5eSRod Evans * values by name, and omit the AV_ prefix. 81808278a5eSRod Evans */ 81908278a5eSRod Evans #define ELFCAP_STYLE ELFCAP_STYLE_LC | ELFCAP_STYLE_F_ICMP 82008278a5eSRod Evans 82108278a5eSRod Evans /* 82208278a5eSRod Evans * To aid in the development and testing of capabilities, an alternative system 82308278a5eSRod Evans * capabilities group can be specified. This alternative set is initialized 82408278a5eSRod Evans * from the system capabilities that are normally used to validate all object 82508278a5eSRod Evans * loading. However, the user can disable, enable or override flags within 82608278a5eSRod Evans * this alternative set, and thus affect object loading. 82708278a5eSRod Evans * 82808278a5eSRod Evans * This technique is usually combined with defining the family of objects 82908278a5eSRod Evans * that should be compared against this alternative set. Without defining the 83008278a5eSRod Evans * family of objects, all objects loaded by ld.so.1 are validated against the 83108278a5eSRod Evans * alternative set. This can prevent the loading of critical system objects 83208278a5eSRod Evans * like libc, and thus prevent process execution. 83308278a5eSRod Evans */ 83408278a5eSRod Evans typedef enum { 83508278a5eSRod Evans CAP_OVERRIDE = 0, /* override existing capabilities */ 83608278a5eSRod Evans CAP_ENABLE = 1, /* enable capabilities */ 83708278a5eSRod Evans CAP_DISABLE = 2 /* disable capabilities */ 83808278a5eSRod Evans } cap_mode; 83908278a5eSRod Evans 84008278a5eSRod Evans static struct { 84108278a5eSRod Evans elfcap_mask_t cs_val[3]; /* value settings, and indicator for */ 84208278a5eSRod Evans int cs_set[3]; /* OVERRIDE, ENABLE and DISABLE */ 84308278a5eSRod Evans elfcap_mask_t *cs_aval; /* alternative variable for final */ 84408278a5eSRod Evans /* update */ 84508278a5eSRod Evans } cap_settings[3] = { 84608278a5eSRod Evans { { 0, 0, 0 }, { 0, 0, 0 }, NULL }, /* CA_SUNW_HW_1 */ 84708278a5eSRod Evans { { 0, 0, 0 }, { 0, 0, 0 }, NULL }, /* CA_SUNW_SF_1 */ 84808278a5eSRod Evans { { 0, 0, 0 }, { 0, 0, 0 }, NULL } /* CA_SUNW_HW_2 */ 84908278a5eSRod Evans }; 85008278a5eSRod Evans 85108278a5eSRod Evans static int 85208278a5eSRod Evans cap_modify(Xword tag, const char *str) 85308278a5eSRod Evans { 85408278a5eSRod Evans char *caps, *ptr, *next; 85508278a5eSRod Evans cap_mode mode = CAP_OVERRIDE; 85608278a5eSRod Evans Xword ndx; 85708278a5eSRod Evans 85808278a5eSRod Evans if ((caps = strdup(str)) == NULL) 85908278a5eSRod Evans return (0); 86008278a5eSRod Evans 861cc4ec439SRichard Lowe for (ptr = strtok_r(caps, MSG_ORIG(MSG_CAP_DELIMIT), &next); 862cc4ec439SRichard Lowe ptr != NULL; 863cc4ec439SRichard Lowe ptr = strtok_r(NULL, MSG_ORIG(MSG_CAP_DELIMIT), &next)) { 86408278a5eSRod Evans Xword val = 0; 86508278a5eSRod Evans 86608278a5eSRod Evans /* 86708278a5eSRod Evans * Determine whether this token should be enabled (+), 86808278a5eSRod Evans * disabled (-), or override any existing settings. 86908278a5eSRod Evans */ 87008278a5eSRod Evans if (*ptr == '+') { 87108278a5eSRod Evans mode = CAP_ENABLE; 87208278a5eSRod Evans ptr++; 87308278a5eSRod Evans } else if (*ptr == '-') { 87408278a5eSRod Evans mode = CAP_DISABLE; 87508278a5eSRod Evans ptr++; 87608278a5eSRod Evans } 87708278a5eSRod Evans 87808278a5eSRod Evans /* 87908278a5eSRod Evans * Process the capabilities as directed by the calling tag. 88008278a5eSRod Evans */ 88108278a5eSRod Evans switch (tag) { 88208278a5eSRod Evans case CA_SUNW_HW_1: 88308278a5eSRod Evans /* 88408278a5eSRod Evans * Determine whether the capabilities string matches 88508278a5eSRod Evans * a known hardware capability mask. Note, the caller 88608278a5eSRod Evans * indicates that these are hardware capabilities by 88708278a5eSRod Evans * passing in the CA_SUNW_HW_1 tag. However, the 88808278a5eSRod Evans * tokens could be CA_SUNW_HW_1 or CA_SUNW_HW_2. 88908278a5eSRod Evans */ 89008278a5eSRod Evans if ((val = (Xword)elfcap_hw2_from_str(ELFCAP_STYLE, 89108278a5eSRod Evans ptr, M_MACH)) != 0) { 89208278a5eSRod Evans ndx = CA_SUNW_HW_2; 89308278a5eSRod Evans break; 89408278a5eSRod Evans } 89508278a5eSRod Evans if ((val = (Xword)elfcap_hw1_from_str(ELFCAP_STYLE, 89608278a5eSRod Evans ptr, M_MACH)) != 0) 89708278a5eSRod Evans ndx = CA_SUNW_HW_1; 89808278a5eSRod Evans break; 89908278a5eSRod Evans case CA_SUNW_SF_1: 90008278a5eSRod Evans /* 90108278a5eSRod Evans * Determine whether the capabilities string matches a 90208278a5eSRod Evans * known software capability mask. Note, the callers 90308278a5eSRod Evans * indication of what capabilities to process are 90408278a5eSRod Evans * triggered by a tag of CA_SUNW_SF_1, but the tokens 90508278a5eSRod Evans * processed could be CA_SUNW_SF_1, CA_SUNW_SF_2, etc. 90608278a5eSRod Evans */ 90708278a5eSRod Evans if ((val = (Xword)elfcap_sf1_from_str(ELFCAP_STYLE, 90808278a5eSRod Evans ptr, M_MACH)) != 0) 90908278a5eSRod Evans ndx = CA_SUNW_SF_1; 91008278a5eSRod Evans break; 91108278a5eSRod Evans } 91208278a5eSRod Evans 91308278a5eSRod Evans /* 91408278a5eSRod Evans * If a capabilities token has not been matched, interpret the 91508278a5eSRod Evans * string as a number. To provide for setting the various 91608278a5eSRod Evans * families (CA_SUNW_HW_1, CA_SUNW_HW_2), the number can be 91708278a5eSRod Evans * prefixed with the (bracketed) family index. 91808278a5eSRod Evans * 91908278a5eSRod Evans * LD_HWCAP=[1]0x40 sets CA_SUNW_HW_1 with 0x40 92008278a5eSRod Evans * LD_HWCAP=[2]0x80 sets CA_SUNW_HW_2 with 0x80 92108278a5eSRod Evans * 92208278a5eSRod Evans * Invalid indexes are ignored. 92308278a5eSRod Evans */ 92408278a5eSRod Evans if (val == 0) { 925*9d4bc394SRichard Lowe char *end; 926*9d4bc394SRichard Lowe 92708278a5eSRod Evans if ((*ptr == '[') && (*(ptr + 2) == ']')) { 92808278a5eSRod Evans if (*(ptr + 1) == '1') { 92908278a5eSRod Evans ndx = tag; 93008278a5eSRod Evans ptr += 3; 93108278a5eSRod Evans } else if (*(ptr + 1) == '2') { 93208278a5eSRod Evans if (tag == CA_SUNW_HW_1) { 93308278a5eSRod Evans ndx = CA_SUNW_HW_2; 93408278a5eSRod Evans ptr += 3; 93508278a5eSRod Evans } else { 93608278a5eSRod Evans /* invalid index */ 93708278a5eSRod Evans continue; 93808278a5eSRod Evans } 93908278a5eSRod Evans } else { 94008278a5eSRod Evans /* invalid index */ 94108278a5eSRod Evans continue; 94208278a5eSRod Evans } 94308278a5eSRod Evans } else 94408278a5eSRod Evans ndx = tag; 94508278a5eSRod Evans 94608278a5eSRod Evans errno = 0; 947*9d4bc394SRichard Lowe if (((val = strtol(ptr, &end, 16)) == 0) && errno) 948*9d4bc394SRichard Lowe continue; 949*9d4bc394SRichard Lowe 950*9d4bc394SRichard Lowe /* 951*9d4bc394SRichard Lowe * If the value wasn't an entirely valid hexadecimal 952*9d4bc394SRichard Lowe * integer, assume it was intended as a capability 953*9d4bc394SRichard Lowe * name and skip it. 954*9d4bc394SRichard Lowe */ 955*9d4bc394SRichard Lowe if (*end != '\0') { 956*9d4bc394SRichard Lowe eprintf(NULL, ERR_WARNING, 957*9d4bc394SRichard Lowe MSG_INTL(MSG_CAP_IGN_UNKCAP), ptr); 95808278a5eSRod Evans continue; 95908278a5eSRod Evans } 960*9d4bc394SRichard Lowe } 961*9d4bc394SRichard Lowe 96208278a5eSRod Evans cap_settings[ndx - 1].cs_val[mode] |= val; 96308278a5eSRod Evans cap_settings[ndx - 1].cs_set[mode]++; 96408278a5eSRod Evans 965cc4ec439SRichard Lowe } 96608278a5eSRod Evans 96708278a5eSRod Evans /* 96808278a5eSRod Evans * If the "override" token was supplied, set the alternative 96908278a5eSRod Evans * system capabilities, then enable or disable others. 97008278a5eSRod Evans */ 97108278a5eSRod Evans for (ndx = 0; ndx < CA_SUNW_HW_2; ndx++) { 97208278a5eSRod Evans if (cap_settings[ndx].cs_set[CAP_OVERRIDE]) 97308278a5eSRod Evans *(cap_settings[ndx].cs_aval) = 97408278a5eSRod Evans cap_settings[ndx].cs_val[CAP_OVERRIDE]; 97508278a5eSRod Evans if (cap_settings[ndx].cs_set[CAP_ENABLE]) 97608278a5eSRod Evans *(cap_settings[ndx].cs_aval) |= 97708278a5eSRod Evans cap_settings[ndx].cs_val[CAP_ENABLE]; 97808278a5eSRod Evans if (cap_settings[ndx].cs_set[CAP_DISABLE]) 97908278a5eSRod Evans *(cap_settings[ndx].cs_aval) &= 98008278a5eSRod Evans ~cap_settings[ndx].cs_val[CAP_DISABLE]; 98108278a5eSRod Evans } 98208278a5eSRod Evans free(caps); 98308278a5eSRod Evans return (1); 98408278a5eSRod Evans } 98508278a5eSRod Evans #undef ELFCAP_STYLE 98608278a5eSRod Evans 98708278a5eSRod Evans /* 98808278a5eSRod Evans * Create an AVL tree of objects that are to be validated against an alternative 98908278a5eSRod Evans * system capabilities value. 99008278a5eSRod Evans */ 99108278a5eSRod Evans static int 99208278a5eSRod Evans cap_files(const char *str) 99308278a5eSRod Evans { 99408278a5eSRod Evans char *caps, *name, *next; 99508278a5eSRod Evans 99608278a5eSRod Evans if ((caps = strdup(str)) == NULL) 99708278a5eSRod Evans return (0); 99808278a5eSRod Evans 999cc4ec439SRichard Lowe for (name = strtok_r(caps, MSG_ORIG(MSG_CAP_DELIMIT), &next); 1000cc4ec439SRichard Lowe name != NULL; 1001cc4ec439SRichard Lowe name = strtok_r(NULL, MSG_ORIG(MSG_CAP_DELIMIT), &next)) { 100208278a5eSRod Evans avl_index_t where; 100308278a5eSRod Evans PathNode *pnp; 100408278a5eSRod Evans uint_t hash = sgs_str_hash(name); 100508278a5eSRod Evans 100608278a5eSRod Evans /* 100708278a5eSRod Evans * Determine whether this pathname has already been recorded. 100808278a5eSRod Evans */ 100908278a5eSRod Evans if (pnavl_recorded(&capavl, name, hash, &where)) 101008278a5eSRod Evans continue; 101108278a5eSRod Evans 101208278a5eSRod Evans if ((pnp = calloc(sizeof (PathNode), 1)) != NULL) { 101308278a5eSRod Evans pnp->pn_name = name; 101408278a5eSRod Evans pnp->pn_hash = hash; 101508278a5eSRod Evans avl_insert(capavl, pnp, where); 101608278a5eSRod Evans } 1017cc4ec439SRichard Lowe } 101808278a5eSRod Evans 101908278a5eSRod Evans return (1); 102008278a5eSRod Evans } 102108278a5eSRod Evans 102208278a5eSRod Evans /* 102308278a5eSRod Evans * Set alternative system capabilities. A user can establish alternative system 102408278a5eSRod Evans * capabilities from the environment, or from a configuration file. This 102508278a5eSRod Evans * routine is called in each instance. Environment variables only set the 102608278a5eSRod Evans * replaceable (rpl) variables. Configuration files can set both replaceable 102708278a5eSRod Evans * (rpl) and permanent (prm) variables. 102808278a5eSRod Evans */ 102908278a5eSRod Evans int 103008278a5eSRod Evans cap_alternative(void) 103108278a5eSRod Evans { 103208278a5eSRod Evans /* 103308278a5eSRod Evans * If no capabilities have been set, we're done. 103408278a5eSRod Evans */ 103508278a5eSRod Evans if ((rpl_hwcap == NULL) && (rpl_sfcap == NULL) && 103608278a5eSRod Evans (rpl_machcap == NULL) && (rpl_platcap == NULL) && 103708278a5eSRod Evans (prm_hwcap == NULL) && (prm_sfcap == NULL) && 103808278a5eSRod Evans (prm_machcap == NULL) && (prm_platcap == NULL)) 103908278a5eSRod Evans return (1); 104008278a5eSRod Evans 104108278a5eSRod Evans /* 104208278a5eSRod Evans * If the user has requested to modify any capabilities, establish a 104308278a5eSRod Evans * unique set from the present system capabilities. 104408278a5eSRod Evans */ 104508278a5eSRod Evans if ((alt_scapset = malloc(sizeof (Syscapset))) == NULL) 104608278a5eSRod Evans return (0); 104708278a5eSRod Evans *alt_scapset = *org_scapset; 104808278a5eSRod Evans 104908278a5eSRod Evans cap_settings[CA_SUNW_HW_1 - 1].cs_aval = &alt_scapset->sc_hw_1; 105008278a5eSRod Evans cap_settings[CA_SUNW_SF_1 - 1].cs_aval = &alt_scapset->sc_sf_1; 105108278a5eSRod Evans cap_settings[CA_SUNW_HW_2 - 1].cs_aval = &alt_scapset->sc_hw_2; 105208278a5eSRod Evans 105308278a5eSRod Evans /* 105408278a5eSRod Evans * Process any replaceable variables. 105508278a5eSRod Evans */ 105608278a5eSRod Evans if (rpl_hwcap && (cap_modify(CA_SUNW_HW_1, rpl_hwcap) == 0)) 105708278a5eSRod Evans return (0); 105808278a5eSRod Evans if (rpl_sfcap && (cap_modify(CA_SUNW_SF_1, rpl_sfcap) == 0)) 105908278a5eSRod Evans return (0); 106008278a5eSRod Evans 106108278a5eSRod Evans if (rpl_platcap) { 106208278a5eSRod Evans alt_scapset->sc_plat = (char *)rpl_platcap; 106308278a5eSRod Evans alt_scapset->sc_platsz = strlen(rpl_platcap); 106408278a5eSRod Evans } 106508278a5eSRod Evans if (rpl_machcap) { 106608278a5eSRod Evans alt_scapset->sc_mach = (char *)rpl_machcap; 106708278a5eSRod Evans alt_scapset->sc_machsz = strlen(rpl_machcap); 106808278a5eSRod Evans } 106908278a5eSRod Evans 107008278a5eSRod Evans if (rpl_cap_files && (cap_files(rpl_cap_files) == 0)) 107108278a5eSRod Evans return (0); 107208278a5eSRod Evans 107308278a5eSRod Evans /* 107408278a5eSRod Evans * Process any permanent variables. 107508278a5eSRod Evans */ 107608278a5eSRod Evans if (prm_hwcap && (cap_modify(CA_SUNW_HW_1, prm_hwcap) == 0)) 107708278a5eSRod Evans return (0); 107808278a5eSRod Evans if (prm_sfcap && (cap_modify(CA_SUNW_SF_1, prm_sfcap) == 0)) 107908278a5eSRod Evans return (0); 108008278a5eSRod Evans 108108278a5eSRod Evans if (prm_platcap) { 108208278a5eSRod Evans alt_scapset->sc_plat = (char *)prm_platcap; 108308278a5eSRod Evans alt_scapset->sc_platsz = strlen(prm_platcap); 108408278a5eSRod Evans } 108508278a5eSRod Evans if (prm_machcap) { 108608278a5eSRod Evans alt_scapset->sc_mach = (char *)prm_machcap; 108708278a5eSRod Evans alt_scapset->sc_machsz = strlen(prm_machcap); 108808278a5eSRod Evans } 108908278a5eSRod Evans 109008278a5eSRod Evans if (prm_cap_files && (cap_files(prm_cap_files) == 0)) 109108278a5eSRod Evans return (0); 109208278a5eSRod Evans 109308278a5eSRod Evans /* 109408278a5eSRod Evans * Reset the replaceable variables. If this is the environment variable 109508278a5eSRod Evans * processing, these variables are now available for configuration file 109608278a5eSRod Evans * initialization. 109708278a5eSRod Evans */ 109808278a5eSRod Evans rpl_hwcap = rpl_sfcap = rpl_machcap = rpl_platcap = 109908278a5eSRod Evans rpl_cap_files = NULL; 110008278a5eSRod Evans 110108278a5eSRod Evans return (1); 110208278a5eSRod Evans } 110308278a5eSRod Evans 110408278a5eSRod Evans /* 110508278a5eSRod Evans * Take the index from a Capinfo entry and determine the associated capabilities 110608278a5eSRod Evans * set. Verify that the capabilities are available for this system. 110708278a5eSRod Evans */ 110808278a5eSRod Evans static int 110908278a5eSRod Evans sym_cap_check(Cap *cptr, uint_t cndx, Syscapset *bestcapset, Rt_map *lmp, 111008278a5eSRod Evans const char *name, uint_t ndx) 111108278a5eSRod Evans { 111208278a5eSRod Evans Syscapset *scapset; 111308278a5eSRod Evans int totplat, ivlplat, totmach, ivlmach, capfail = 0; 111408278a5eSRod Evans 111508278a5eSRod Evans /* 111608278a5eSRod Evans * Determine whether this file requires validation against alternative 111708278a5eSRod Evans * system capabilities. 111808278a5eSRod Evans */ 111908278a5eSRod Evans if ((FLAGS1(lmp) & FL1_RT_ALTCHECK) == 0) 112008278a5eSRod Evans cap_check_lmp_init(lmp); 112108278a5eSRod Evans 112208278a5eSRod Evans if (FLAGS1(lmp) & FL1_RT_ALTCAP) 112308278a5eSRod Evans scapset = alt_scapset; 112408278a5eSRod Evans else 112508278a5eSRod Evans scapset = org_scapset; 112608278a5eSRod Evans 112708278a5eSRod Evans totplat = ivlplat = totmach = ivlmach = 0; 112808278a5eSRod Evans 112908278a5eSRod Evans /* 113008278a5eSRod Evans * A capabilities index points to a capabilities group that can consist 113108278a5eSRod Evans * of one or more capabilities, terminated with a CA_SUNW_NULL entry. 113208278a5eSRod Evans */ 113308278a5eSRod Evans for (cptr += cndx; cptr->c_tag != CA_SUNW_NULL; cptr++) { 113408278a5eSRod Evans Xword val = cptr->c_un.c_val; 113508278a5eSRod Evans char *str; 113608278a5eSRod Evans 113708278a5eSRod Evans switch (cptr->c_tag) { 113808278a5eSRod Evans case CA_SUNW_HW_1: 11394e12d685SRod Evans /* 11404e12d685SRod Evans * Remove any historic values that should not be 11414e12d685SRod Evans * involved with any validation. 11424e12d685SRod Evans */ 11434e12d685SRod Evans val &= ~AV_HW1_IGNORE; 11444e12d685SRod Evans 114508278a5eSRod Evans bestcapset->sc_hw_1 = val; 114608278a5eSRod Evans DBG_CALL(Dbg_syms_cap_lookup(lmp, DBG_CAP_HW_1, 114708278a5eSRod Evans name, ndx, M_MACH, bestcapset)); 114808278a5eSRod Evans 114908278a5eSRod Evans if (hwcap1_check(scapset, val, NULL) == 0) 115008278a5eSRod Evans capfail++; 115108278a5eSRod Evans break; 115208278a5eSRod Evans case CA_SUNW_SF_1: 115308278a5eSRod Evans bestcapset->sc_sf_1 = val; 115408278a5eSRod Evans DBG_CALL(Dbg_syms_cap_lookup(lmp, DBG_CAP_SF_1, 115508278a5eSRod Evans name, ndx, M_MACH, bestcapset)); 115608278a5eSRod Evans 115708278a5eSRod Evans if (sfcap1_check(scapset, val, NULL) == 0) 115808278a5eSRod Evans capfail++; 115908278a5eSRod Evans break; 116008278a5eSRod Evans case CA_SUNW_HW_2: 116108278a5eSRod Evans bestcapset->sc_hw_2 = val; 116208278a5eSRod Evans DBG_CALL(Dbg_syms_cap_lookup(lmp, DBG_CAP_HW_2, 116308278a5eSRod Evans name, ndx, M_MACH, bestcapset)); 116408278a5eSRod Evans 116508278a5eSRod Evans if (hwcap2_check(scapset, val, NULL) == 0) 116608278a5eSRod Evans capfail++; 116708278a5eSRod Evans break; 116808278a5eSRod Evans case CA_SUNW_PLAT: 116908278a5eSRod Evans /* 117008278a5eSRod Evans * A capabilities set can define multiple platform names 117108278a5eSRod Evans * that are appropriate. Only if all the names are 117208278a5eSRod Evans * deemed invalid is the group determined inappropriate. 117308278a5eSRod Evans */ 117408278a5eSRod Evans if (totplat == ivlplat) { 117508278a5eSRod Evans totplat++; 117608278a5eSRod Evans 117708278a5eSRod Evans str = STRTAB(lmp) + val; 117808278a5eSRod Evans bestcapset->sc_plat = str; 117908278a5eSRod Evans 118008278a5eSRod Evans DBG_CALL(Dbg_syms_cap_lookup(lmp, DBG_CAP_PLAT, 118108278a5eSRod Evans name, ndx, M_MACH, bestcapset)); 118208278a5eSRod Evans 118308278a5eSRod Evans if (platcap_check(scapset, str, NULL) == 0) 118408278a5eSRod Evans ivlplat++; 118508278a5eSRod Evans } 118608278a5eSRod Evans break; 118708278a5eSRod Evans case CA_SUNW_MACH: 118808278a5eSRod Evans /* 118908278a5eSRod Evans * A capabilities set can define multiple machine names 119008278a5eSRod Evans * that are appropriate. Only if all the names are 119108278a5eSRod Evans * deemed invalid is the group determined inappropriate. 119208278a5eSRod Evans */ 119308278a5eSRod Evans if (totmach == ivlmach) { 119408278a5eSRod Evans totmach++; 119508278a5eSRod Evans 119608278a5eSRod Evans str = STRTAB(lmp) + val; 119708278a5eSRod Evans bestcapset->sc_mach = str; 119808278a5eSRod Evans 119908278a5eSRod Evans DBG_CALL(Dbg_syms_cap_lookup(lmp, DBG_CAP_MACH, 120008278a5eSRod Evans name, ndx, M_MACH, bestcapset)); 120108278a5eSRod Evans 120208278a5eSRod Evans if (machcap_check(scapset, str, NULL) == 0) 120308278a5eSRod Evans ivlmach++; 120408278a5eSRod Evans } 120508278a5eSRod Evans break; 120608278a5eSRod Evans default: 120708278a5eSRod Evans break; 120808278a5eSRod Evans } 120908278a5eSRod Evans } 121008278a5eSRod Evans 121108278a5eSRod Evans /* 121208278a5eSRod Evans * If any platform definitions, or machine definitions were found, and 121308278a5eSRod Evans * all were invalid, indicate that the object is inappropriate. 121408278a5eSRod Evans */ 121508278a5eSRod Evans if (capfail || (totplat && (totplat == ivlplat)) || 121608278a5eSRod Evans (totmach && (totmach == ivlmach))) { 121708278a5eSRod Evans DBG_CALL(Dbg_syms_cap_lookup(lmp, DBG_CAP_REJECTED, name, ndx, 121808278a5eSRod Evans M_MACH, NULL)); 121908278a5eSRod Evans return (0); 122008278a5eSRod Evans } 122108278a5eSRod Evans 122208278a5eSRod Evans DBG_CALL(Dbg_syms_cap_lookup(lmp, DBG_CAP_CANDIDATE, name, ndx, 122308278a5eSRod Evans M_MACH, NULL)); 122408278a5eSRod Evans return (1); 122508278a5eSRod Evans } 122608278a5eSRod Evans 122708278a5eSRod Evans /* 122808278a5eSRod Evans * Determine whether a symbols capabilities are more significant than any that 122908278a5eSRod Evans * have already been validated. The precedence of capabilities are: 123008278a5eSRod Evans * 123108278a5eSRod Evans * PLATCAP -> MACHCAP -> HWCAP_2 -> HWCAP_1 123208278a5eSRod Evans * 123308278a5eSRod Evans * 123408278a5eSRod Evans * Presently we make no comparisons of software capabilities. However, should 123508278a5eSRod Evans * this symbol capability have required the SF1_SUNW_ADDR32 attribute, then 123608278a5eSRod Evans * this would have been validated as appropriate or not. 123708278a5eSRod Evans * 123808278a5eSRod Evans * bestcapset is the presently available 'best' capabilities group, and 123908278a5eSRod Evans * symcapset is the present capabilities group under investigation. Return 0 124008278a5eSRod Evans * if the bestcapset should remain in affect, or 1 if the symcapset is better. 124108278a5eSRod Evans */ 124208278a5eSRod Evans inline static int 124308278a5eSRod Evans is_sym_the_best(Syscapset *bestcapset, Syscapset *symcapset) 124408278a5eSRod Evans { 124508278a5eSRod Evans /* 124608278a5eSRod Evans * Check any platform capability. If the new symbol isn't associated 124708278a5eSRod Evans * with a CA_SUNW_PLAT capability, and the best symbol is, then retain 124808278a5eSRod Evans * the best capabilities group. If the new symbol is associated with a 124908278a5eSRod Evans * CA_SUNW_PLAT capability, and the best symbol isn't, then the new 125008278a5eSRod Evans * symbol needs to be taken. 125108278a5eSRod Evans */ 125208278a5eSRod Evans if (bestcapset->sc_plat && (symcapset->sc_plat == NULL)) 125308278a5eSRod Evans return (0); 125408278a5eSRod Evans 125508278a5eSRod Evans if ((bestcapset->sc_plat == NULL) && symcapset->sc_plat) 125608278a5eSRod Evans return (1); 125708278a5eSRod Evans 125808278a5eSRod Evans /* 125908278a5eSRod Evans * Check any machine name capability. If the new symbol isn't 126008278a5eSRod Evans * associated with a CA_SUNW_MACH capability, and the best symbol is, 126108278a5eSRod Evans * then retain the best capabilities group. If the new symbol is 126208278a5eSRod Evans * associated with a CA_SUNW_MACH capability, and the best symbol isn't, 126308278a5eSRod Evans * then the new symbol needs to be taken. 126408278a5eSRod Evans */ 126508278a5eSRod Evans if (bestcapset->sc_mach && (symcapset->sc_mach == NULL)) 126608278a5eSRod Evans return (0); 126708278a5eSRod Evans 126808278a5eSRod Evans if ((bestcapset->sc_mach == NULL) && symcapset->sc_mach) 126908278a5eSRod Evans return (1); 127008278a5eSRod Evans 127108278a5eSRod Evans /* 127208278a5eSRod Evans * Check the hardware capabilities. If the best symbols CA_SUNW_HW_2 127308278a5eSRod Evans * capabilities are greater than the new symbols capabilities, then 127408278a5eSRod Evans * retain the best capabilities group. If the new symbols CA_SUNW_HW_2 127508278a5eSRod Evans * capabilities are greater than the best symbol, then the new symbol 127608278a5eSRod Evans * needs to be taken. 127708278a5eSRod Evans */ 127808278a5eSRod Evans if (bestcapset->sc_hw_2 > symcapset->sc_hw_2) 127908278a5eSRod Evans return (0); 128008278a5eSRod Evans 128108278a5eSRod Evans if (bestcapset->sc_hw_2 < symcapset->sc_hw_2) 128208278a5eSRod Evans return (1); 128308278a5eSRod Evans 128408278a5eSRod Evans /* 128508278a5eSRod Evans * Check the remaining hardware capabilities. If the best symbols 128608278a5eSRod Evans * CA_SUNW_HW_1 capabilities are greater than the new symbols 128708278a5eSRod Evans * capabilities, then retain the best capabilities group. If the new 128808278a5eSRod Evans * symbols CA_SUNW_HW_1 capabilities are greater than the best symbol, 128908278a5eSRod Evans * then the new symbol needs to be taken. 129008278a5eSRod Evans */ 129108278a5eSRod Evans if (bestcapset->sc_hw_1 > symcapset->sc_hw_1) 129208278a5eSRod Evans return (0); 129308278a5eSRod Evans 129408278a5eSRod Evans if (bestcapset->sc_hw_1 < symcapset->sc_hw_1) 129508278a5eSRod Evans return (1); 129608278a5eSRod Evans 129708278a5eSRod Evans /* 129808278a5eSRod Evans * Both capabilities are the same. Retain the best on a first-come 129908278a5eSRod Evans * first-served basis. 130008278a5eSRod Evans */ 130108278a5eSRod Evans return (0); 130208278a5eSRod Evans } 130308278a5eSRod Evans 130408278a5eSRod Evans /* 130508278a5eSRod Evans * Initiate symbol capabilities processing. If an initial symbol lookup 130608278a5eSRod Evans * results in binding to a symbol that has an associated SUNW_capinfo entry, 130708278a5eSRod Evans * we arrive here. 130808278a5eSRod Evans * 130908278a5eSRod Evans * The standard model is that this initial symbol is the lead capabilities 131008278a5eSRod Evans * symbol (defined as CAPINFO_SUNW_GLOB) of a capabilities family. This lead 131108278a5eSRod Evans * symbol's SUNW_capinfo information points to the SUNW_capchain entry that 131208278a5eSRod Evans * provides the family symbol indexes. We traverse this chain, looking at 131308278a5eSRod Evans * each family member, to discover the best capabilities instance. This 131408278a5eSRod Evans * instance name and symbol information is returned to establish the final 131508278a5eSRod Evans * symbol binding. 131608278a5eSRod Evans * 131708278a5eSRod Evans * If the symbol that got us here is not CAPINFO_SUNW_GLOB, then we've bound 131808278a5eSRod Evans * directly to a capabilities symbol which must be verified. This is not the 131908278a5eSRod Evans * model created by ld(1) using -z symbolcap, but might be created directly 132008278a5eSRod Evans * within a relocatable object by the compilation system. 132108278a5eSRod Evans */ 132208278a5eSRod Evans int 132308278a5eSRod Evans cap_match(Sresult *srp, uint_t symndx, Sym *symtabptr, char *strtabptr) 132408278a5eSRod Evans { 132508278a5eSRod Evans Rt_map *ilmp = srp->sr_dmap; 132608278a5eSRod Evans Sym *bsym = NULL; 132708278a5eSRod Evans const char *bname; 132808278a5eSRod Evans Syscapset bestcapset = { 0 }; 132908278a5eSRod Evans Cap *cap; 133008278a5eSRod Evans Capchain *capchain; 133108278a5eSRod Evans uchar_t grpndx; 133208278a5eSRod Evans uint_t ochainndx, nchainndx, bndx; 133308278a5eSRod Evans 133408278a5eSRod Evans cap = CAP(ilmp); 133508278a5eSRod Evans capchain = CAPCHAIN(ilmp); 133608278a5eSRod Evans 133708278a5eSRod Evans grpndx = (uchar_t)ELF_C_GROUP(CAPINFO(ilmp)[symndx]); 133808278a5eSRod Evans 133908278a5eSRod Evans /* 134008278a5eSRod Evans * If this symbols capability group is not a lead symbol, then simply 134108278a5eSRod Evans * verify the symbol. 134208278a5eSRod Evans */ 134308278a5eSRod Evans if (grpndx != CAPINFO_SUNW_GLOB) { 134408278a5eSRod Evans Syscapset symcapset = { 0 }; 134508278a5eSRod Evans 134608278a5eSRod Evans return (sym_cap_check(cap, grpndx, &symcapset, ilmp, 134708278a5eSRod Evans srp->sr_name, symndx)); 134808278a5eSRod Evans } 134908278a5eSRod Evans 135008278a5eSRod Evans /* 135108278a5eSRod Evans * If there is no capabilities chain, return the lead symbol. 135208278a5eSRod Evans */ 135308278a5eSRod Evans if (capchain == NULL) 135408278a5eSRod Evans return (1); 135508278a5eSRod Evans 135608278a5eSRod Evans ochainndx = (uint_t)ELF_C_SYM(CAPINFO(ilmp)[symndx]); 135708278a5eSRod Evans 135808278a5eSRod Evans /* 135908278a5eSRod Evans * If there is only one member for this family, take it. Once a family 136008278a5eSRod Evans * has been processed, the best family instance is written to the head 136108278a5eSRod Evans * of the chain followed by a null entry. This caching ensures that the 136208278a5eSRod Evans * same family comparison doesn't have to be undertaken more than once. 136308278a5eSRod Evans */ 136408278a5eSRod Evans if (capchain[ochainndx] && (capchain[ochainndx + 1] == 0)) { 136508278a5eSRod Evans Sym *fsym = symtabptr + capchain[ochainndx]; 136608278a5eSRod Evans const char *fname = strtabptr + fsym->st_name; 136708278a5eSRod Evans 136808278a5eSRod Evans DBG_CALL(Dbg_syms_cap_lookup(ilmp, DBG_CAP_USED, fname, 136908278a5eSRod Evans capchain[ochainndx], M_MACH, NULL)); 137008278a5eSRod Evans 137108278a5eSRod Evans srp->sr_sym = fsym; 137208278a5eSRod Evans srp->sr_name = fname; 137308278a5eSRod Evans return (1); 137408278a5eSRod Evans } 137508278a5eSRod Evans 137608278a5eSRod Evans /* 137708278a5eSRod Evans * As this symbol is the lead symbol of a capabilities family, it is 137808278a5eSRod Evans * considered the generic member, and therefore forms the basic 137908278a5eSRod Evans * fall-back for the capabilities family. 138008278a5eSRod Evans */ 138108278a5eSRod Evans DBG_CALL(Dbg_syms_cap_lookup(ilmp, DBG_CAP_DEFAULT, srp->sr_name, 138208278a5eSRod Evans symndx, M_MACH, NULL)); 138308278a5eSRod Evans bsym = srp->sr_sym; 138408278a5eSRod Evans bname = srp->sr_name; 138508278a5eSRod Evans bndx = symndx; 138608278a5eSRod Evans 138708278a5eSRod Evans /* 138808278a5eSRod Evans * Traverse the capabilities chain analyzing each family member. 138908278a5eSRod Evans */ 139008278a5eSRod Evans for (nchainndx = ochainndx + 1, symndx = capchain[nchainndx]; symndx; 139108278a5eSRod Evans nchainndx++, symndx = capchain[nchainndx]) { 139208278a5eSRod Evans Sym *nsym = symtabptr + symndx; 139308278a5eSRod Evans const char *nname = strtabptr + nsym->st_name; 139408278a5eSRod Evans Syscapset symcapset = { 0 }; 139508278a5eSRod Evans 139608278a5eSRod Evans if ((grpndx = 139708278a5eSRod Evans (uchar_t)ELF_C_GROUP(CAPINFO(ilmp)[symndx])) == 0) 139808278a5eSRod Evans continue; 139908278a5eSRod Evans 140008278a5eSRod Evans if (sym_cap_check(cap, grpndx, &symcapset, ilmp, 140108278a5eSRod Evans nname, symndx) == 0) 140208278a5eSRod Evans continue; 140308278a5eSRod Evans 140408278a5eSRod Evans /* 140508278a5eSRod Evans * Determine whether a symbol's capabilities are more 140608278a5eSRod Evans * significant than any that have already been validated. 140708278a5eSRod Evans */ 140808278a5eSRod Evans if (is_sym_the_best(&bestcapset, &symcapset)) { 140908278a5eSRod Evans bestcapset = symcapset; 141008278a5eSRod Evans bsym = nsym; 141108278a5eSRod Evans bname = nname; 141208278a5eSRod Evans bndx = symndx; 141308278a5eSRod Evans } 141408278a5eSRod Evans } 141508278a5eSRod Evans 141608278a5eSRod Evans DBG_CALL(Dbg_syms_cap_lookup(ilmp, DBG_CAP_USED, bname, bndx, 141708278a5eSRod Evans M_MACH, NULL)); 141808278a5eSRod Evans 141908278a5eSRod Evans /* 142008278a5eSRod Evans * Having found the best symbol, cache the results by overriding the 142108278a5eSRod Evans * first element of the associated chain. 142208278a5eSRod Evans */ 142308278a5eSRod Evans capchain[ochainndx] = bndx; 142408278a5eSRod Evans capchain[ochainndx + 1] = 0; 142508278a5eSRod Evans 142608278a5eSRod Evans /* 142708278a5eSRod Evans * Update the symbol result information for return to the user. 142808278a5eSRod Evans */ 142908278a5eSRod Evans srp->sr_sym = bsym; 143008278a5eSRod Evans srp->sr_name = bname; 143108278a5eSRod Evans return (1); 143208278a5eSRod Evans } 1433