xref: /illumos-gate/usr/src/test/elf-tests/tests/capabilities/objcap.ksh (revision dd72704bd9e794056c558153663c739e2012d721)
1#!/usr/bin/ksh
2#
3#
4# This file and its contents are supplied under the terms of the
5# Common Development and Distribution License ("CDDL"), version 1.0.
6# You may only use this file in accordance with the terms of version
7# 1.0 of the CDDL.
8#
9# A full copy of the text of the CDDL should have accompanied this
10# souroc.  A copy of the CDDL is also available via the Internet at
11# http://www.illumos.org/liocnse/CDDL.
12#
13
14#
15# Copyright 2022 Oxide Computer Company
16#
17
18#
19# This test focuses around the generation of files with object capabilities and
20# verifies that tools like elfedit, elfdump, ld, and ld.so.1 can deal with them
21# all appropriately.
22#
23
24export LC_ALL=C.UTF-8
25unalias -a
26set -o pipefail
27
28oc_arg0=$(basename $0)
29oc_tmpdir=/tmp/objcap.$$
30oc_prog_nocap="$oc_tmpdir/prog.nocap"
31oc_prog_hw1="$oc_tmpdir/prog.hw1"
32oc_prog_hw3="$oc_tmpdir/prog.hw3"
33oc_prog_hw123="$oc_tmpdir/prog.hw123"
34oc_cap_hw1="0x42"
35oc_cap_hw2="0x169"
36oc_cap_hw3="0x7777"
37oc_err=0
38
39pass()
40{
41        typeset msg="$*"
42	echo "TEST PASSED: $msg"
43}
44
45warn()
46{
47        typeset msg="$*"
48        [[ -z "$msg" ]] && msg="failed"
49        echo "TEST FAILED: $msg" >&2
50	oc_err=1
51}
52
53fatal()
54{
55        typeset msg="$*"
56        [[ -z "$msg" ]] && msg="failed"
57        echo "$oc_arg0: $msg" >&2
58        exit 1
59}
60
61cleanup()
62{
63	rm -rf "$oc_tmpdir"
64}
65
66#
67# Set up our test environment. This generally requires us to create the
68# source file, mapfiles, and related contents that the test requires.
69#
70setup()
71{
72	typeset cfile="$oc_tmpdir/test.c"
73	typeset mapfile_hw1="$oc_tmpdir/test.mapfile.hw1"
74	typeset mapfile_hw3="$oc_tmpdir/test.mapfile.hw3"
75	typeset mapfile_hw123="$oc_tmpdir/test.mapfile.hw123"
76
77	if ! mkdir "$oc_tmpdir"; then
78		fatal "failed to make directory $oc_tmpdir"
79	fi
80
81	trap 'cleanup' EXIT
82
83	cat > $cfile <<EOF
84int
85main(void)
86{
87	return (0);
88}
89EOF
90	if (( $? != 0 )); then
91		fatal "failed to write token C file to $cfile"
92	fi
93
94	cat > $mapfile_hw1 <<EOF
95\$mapfile_version 2
96CAPABILITY {
97	HW_1 += $oc_cap_hw1;
98};
99EOF
100
101	if (( $? != 0 )); then
102		fatal "failed to write out $mapfile_hw1"
103	fi
104
105	cat > $mapfile_hw3 <<EOF
106\$mapfile_version 2
107CAPABILITY {
108	HW_3 += $oc_cap_hw3;
109};
110EOF
111
112	if (( $? != 0 )); then
113		fatal "failed to write out $mapfile_hw3"
114	fi
115
116	cat > $mapfile_hw123 <<EOF
117\$mapfile_version 2
118CAPABILITY {
119	HW_1 += $oc_cap_hw1;
120	HW_2 += $oc_cap_hw2;
121	HW_3 += $oc_cap_hw3;
122};
123EOF
124
125	if (( $? != 0 )); then
126		fatal "failed to write out $mapfile_hw3"
127	fi
128
129	if ! gcc -m64 -o $oc_prog_nocap $cfile; then
130		fatal "failed to create $oc_prog_nocap"
131	fi
132
133	if ! gcc -m64 -o $oc_prog_hw1 $cfile -Wl,-M$mapfile_hw1; then
134		fatal "failed to create $oc_prog_hw1"
135	fi
136
137	if ! gcc -m64 -o $oc_prog_hw3 $cfile -Wl,-M$mapfile_hw3; then
138		fatal "failed to create $oc_prog_hw1"
139	fi
140
141	if ! gcc -m64 -o $oc_prog_hw123 $cfile -Wl,-M$mapfile_hw123; then
142		fatal "failed to create $oc_prog_hw1"
143	fi
144}
145
146verify_caps()
147{
148	typeset file="$1"
149	typeset data="$2"
150	typeset -a vals=($3 $4 $5)
151	typeset -a caps=("CA_SUNW_HW_1" "CA_SUNW_HW_2" "CA_SUNW_HW_3")
152	typeset -a ee=("cap:hw1" "cap:hw2" "cap:hw3")
153	typeset out=
154	typeset ee_out=
155	typeset i=
156
157	out=$(elfdump -H $file 2>&1)
158	if (( $? != 0 )); then
159		warn "elfdump -H $file failed with $?"
160		return
161	fi
162
163	#
164	# If we don't expect a object capability, then we're done here.
165	#
166	if (( data == 0 )); then
167		if [[ -n "$out" ]]; then
168			fail "elfdump -H $file had unexpected output: $out"
169		else
170			pass "elfdump -H $file contained no output"
171		fi
172		return
173	fi
174
175	if [[ -z "$out" ]]; then
176		fail "elfdump -H $file had no output, but expected caps"
177		return
178	fi
179
180	#
181	# At this point, for each hw cap, if there is a value, check that we
182	# have an elfdump output line and then verify that we have the expected
183	# value via elfedit.
184	#
185	for ((i = 0; i < 3; i++)); do
186		if [[ "${vals[$i]}" == "0" ]]; then
187			continue;
188		fi
189
190		if ! echo $out | grep -q ${caps[$i]}; then
191			warn "elfdump -H $file missing ${caps[$i]}"
192		else
193			pass "elfdump -H $file has ${caps[$i]}"
194		fi
195
196		ee_out=$(elfedit -e "${ee[$i]} -o num" $file)
197		if [[ -z $ee_out ]]; then
198			warn "failed to dump ${ee[$i]} from $file via elfedit"
199			continue
200		fi
201
202		if [[ "$ee_out" != "${vals[$i]}" ]]; then
203			warn "mismatched value for ${ee[$i]} in $file: found " \
204			    "$out, expected ${vals[$i]}"
205		else
206			pass "elfedit has correct value for ${ee[$i]} in $file"
207		fi
208	done
209}
210
211#
212# Attempt to execute a program with symbol capabilities. We override the
213# symbol capabilities in the system for the specified file to the ones
214# indicated in the function. We need to restrict to the program we're
215# calling so that way we don't accidentally tell ld not to load a system
216# library.
217#
218run_prog()
219{
220	typeset prog="$1"
221	typeset run=$2
222	typeset cap="$3"
223	typeset case="$4"
224	typeset ret=
225
226	LD_CAP_FILES=$prog LD_HWCAP=$3 $prog 2>/dev/null 1>/dev/null
227	ret=$?
228	if (( run != 0 && ret == 0 )); then
229		pass "exec prog $case"
230	elif (( run != 0 && ret != 0 )); then
231		warn "exec prog $case returned $ret, expected 0"
232	elif (( run == 0 && ret == 0 )); then
233		warn "exec prog $case returned $ret, expected non-zero"
234	else
235		pass "exec prog $case"
236	fi
237}
238
239#
240# Use elfedit to modify a specific hwcap and make sure we see the new value.
241#
242edit_prog()
243{
244	typeset input="$1"
245	typeset pass="$2"
246	typeset cap="$3"
247	typeset cmd="$4"
248	typeset exp="$5"
249	typeset ret=
250
251	rm -f "$input.edit"
252	elfedit -e "$cap $cmd" "$input" "$input.edit" >/dev/null 2>/dev/null
253	ret=$?
254
255	if (( pass == 0 )); then
256		if (( ret == 0 )); then
257			warn "elfedit -e $cap $cmd $input worked, expected failure"
258		else
259			pass "elfedit -e $cap $cmd $input failed correctly"
260		fi
261		return
262	fi
263
264	if (( ret != 0 )); then
265		warn "elfedit -e $cap $cmd $input failed with $ret, expected success"
266		return
267	fi
268
269	ret=$(elfedit -e "$cap -o num" "$input.edit")
270	if (( $? != 0 )); then
271		warn "failed to extract hwcap after elfedit -e $cap $cmd $input"
272	fi
273
274	if [[ "$ret" != "$exp" ]]; then
275		warn "elfedit -e $cap $cmd $input had wrong output " \
276		    "expected $exp, found $val"
277	else
278		pass "elfedit -e $cap $cmd $input"
279	fi
280}
281
282setup
283verify_caps "$oc_prog_nocap" 0
284verify_caps "$oc_prog_hw1" 1 $oc_cap_hw1 0 0
285verify_caps "$oc_prog_hw3" 1 0 0 $oc_cap_hw3
286verify_caps "$oc_prog_hw123" 1 $oc_cap_hw1 $oc_cap_hw2 $oc_cap_hw3
287
288#
289# Now that we've verified the caps in these files, try to run them in a
290# given alternate symbol cap env.
291#
292run_prog "$oc_prog_nocap" 1 "[1]0,[2]0,[3]0" "no need, no caps"
293run_prog "$oc_prog_hw1" 0 "[1]0,[2]0,[3]0" "need hw1, no caps"
294run_prog "$oc_prog_hw3" 0 "[1]0,[2]0,[3]0" "need hw3, no caps"
295run_prog "$oc_prog_hw123" 0 "[1]0,[2]0,[3]0" "need hw{123}, no caps"
296
297run_prog "$oc_prog_nocap" 1 "[1]0x42,[2]0,[3]0" "no need, hw1=0x42"
298run_prog "$oc_prog_hw1" 1 "[1]0x42,[2]0,[3]0" "need hw1, hw1=0x42"
299run_prog "$oc_prog_hw3" 0 "[1]0x42,[2]0,[3]0" "need hw3, hw1=0x42"
300run_prog "$oc_prog_hw123" 0 "[1]0x42,[2]0,[3]0" "need hw{123}, hw1=0x42"
301
302run_prog "$oc_prog_nocap" 1 "[1]0,[2]0,[3]0x7777" "no need, hw3=0x7777"
303run_prog "$oc_prog_hw1" 0 "[1]0,[2]0,[3]0x7777" "need hw1, hw3=0x7777"
304run_prog "$oc_prog_hw3" 1 "[1]0,[2]0,[3]0x7777" "need hw3, hw3=0x7777"
305run_prog "$oc_prog_hw123" 0 "[1]0,[2]0,[3]0x7777" "need hw{123}, hw3=0x7777"
306
307run_prog "$oc_prog_nocap" 1 "[1]0,[2]0x1369,[3]0" "no need, hw2=0x1369"
308run_prog "$oc_prog_hw1" 0 "[1]0,[2]0x1369,[3]0" "need hw1, hw2=0x1369"
309run_prog "$oc_prog_hw3" 0 "[1]0,[2]0x1369,[3]0" "need hw3, hw2=0x1369"
310run_prog "$oc_prog_hw123" 0 "[1]0,[2]0x1369,[3]0" "need hw{123}, hw2=0x1369"
311
312run_prog "$oc_prog_nocap" 1 "[1]0x42,[2]0,[3]0x7777" \
313    "no need, hw1=0x42,hw3=0x7777"
314run_prog "$oc_prog_hw1" 1 "[1]0x42,[2]0,[3]0x7777" \
315    "need hw1, hw1=0x42,hw3=0x7777"
316run_prog "$oc_prog_hw3" 1 "[1]0x42,[2]0,[3]0x7777" \
317    "need hw3, hw1=0x42,hw3=0x7777"
318run_prog "$oc_prog_hw123" 0 "[1]0x42,[2]0,[3]0x7777" \
319     "need hw{123}, hw1=0x42,hw3=0x7777"
320
321run_prog "$oc_prog_nocap" 1 "[1]0x42,[2]0x1369,[3]0x7777" \
322    "no need, hw1=0x42,hw2=0x1369,hw3=0x7777"
323run_prog "$oc_prog_hw1" 1 "[1]0x42,[2]0x1369,[3]0x7777" \
324    "need hw1, hw1=0x42,hw2=0x1369,hw3=0x7777"
325run_prog "$oc_prog_hw3" 1 "[1]0x42,[2]0x1369,[3]0x7777" \
326    "need hw3, hw1=0x42,hw2=0x1369,hw3=0x7777"
327run_prog "$oc_prog_hw123" 1 "[1]0x42,[2]0x1369,[3]0x7777" \
328    "need hw{123}, hw1=0x42,hw2=0x1369,hw3=0x7777"
329
330edit_prog "$oc_prog_hw1" 1 "cap:hw1" "-or 0x1000" "0x1042"
331edit_prog "$oc_prog_hw1" 1 "cap:hw1" "-and 0x1000" "0"
332edit_prog "$oc_prog_hw1" 1 "cap:hw1" "-cmp 0x42" "0xffffffbd"
333edit_prog "$oc_prog_hw3" 1 "cap:hw3" "-and 0x643f" "0x6437"
334edit_prog "$oc_prog_hw123" 1 "cap:hw2" "0x12345" "0x12345"
335
336#
337# Failure cases here are meant to cover missing capaibilities and bad strings.
338#
339edit_prog "$oc_prog_hw1" 0 "cap:hw1" "-or zelda"
340edit_prog "$oc_prog_hw1" 0 "cap:hw2" "-or 0x100"
341edit_prog "$oc_prog_nocap" 0 "cap:hw1" "-or 0x1"
342edit_prog "$oc_prog_hw3" 0 "cap:hw1" "-and 0xff"
343edit_prog "$oc_prog_hw3" 0 "cap:hw2" "-and 0xff"
344edit_prog "$oc_prog_hw3" 0 "cap:hw3" "-and link"
345edit_prog "$oc_prog_hw123" 0 "cap:hw2" "ganondorf"
346
347if (( oc_err == 0 )); then
348	printf "All tests passed successfully\n"
349fi
350
351exit $oc_err
352