xref: /freebsd/tests/sys/kern/jailmeta.sh (revision 30e6e008bc06385a66756bebb41676f4f9017eca)
1#
2# SPDX-License-Identifier: BSD-2-Clause
3#
4# Copyright (c) 2024 SkunkWerks GmbH
5#
6# This software was developed by Igor Ostapenko <igoro@FreeBSD.org>
7# under sponsorship from SkunkWerks GmbH.
8#
9
10setup()
11{
12	# Check if we have enough buffer space for testing
13	if [ $(sysctl -n security.jail.meta_maxbufsize) -lt 128 ]; then
14		atf_skip "sysctl security.jail.meta_maxbufsize must be 128+ for testing."
15	fi
16}
17
18atf_test_case "jail_create" "cleanup"
19jail_create_head()
20{
21	atf_set descr 'Test that metadata can be set upon jail creation with jail(8)'
22	atf_set require.user root
23	atf_set execenv jail
24}
25jail_create_body()
26{
27	setup
28
29	atf_check -s not-exit:0 -e match:"not found" -o ignore \
30	    jls -jj
31
32	atf_check -s exit:0 \
33	    jail -c name=j persist meta="a b c" env="C B A"
34
35	atf_check -s exit:0 -o inline:"a b c\n" \
36	    jls -jj meta
37	atf_check -s exit:0 -o inline:"C B A\n" \
38	    jls -jj env
39}
40jail_create_cleanup()
41{
42	jail -r j
43	return 0
44}
45
46atf_test_case "jail_modify" "cleanup"
47jail_modify_head()
48{
49	atf_set descr 'Test that metadata can be modified after jail creation with jail(8)'
50	atf_set require.user root
51	atf_set execenv jail
52}
53jail_modify_body()
54{
55	setup
56
57	atf_check -s not-exit:0 -e match:"not found" -o ignore \
58	    jls -jj
59
60	atf_check -s exit:0 \
61	    jail -c name=j persist meta="a	b	c" env="CAB"
62
63	atf_check -s exit:0 -o inline:"a	b	c\n" \
64	    jls -jj meta
65	atf_check -s exit:0 -o inline:"CAB\n" \
66	    jls -jj env
67
68	atf_check -s exit:0 \
69	    jail -m name=j meta="t1=A t2=B" env="CAB2"
70
71	atf_check -s exit:0 -o inline:"t1=A t2=B\n" \
72	    jls -jj meta
73	atf_check -s exit:0 -o inline:"CAB2\n" \
74	    jls -jj env
75}
76jail_modify_cleanup()
77{
78	jail -r j
79	return 0
80}
81
82atf_test_case "jail_add" "cleanup"
83jail_add_head()
84{
85	atf_set descr 'Test that metadata can be added to an existing jail with jail(8)'
86	atf_set require.user root
87	atf_set execenv jail
88}
89jail_add_body()
90{
91	setup
92
93	atf_check -s not-exit:0 -e match:"not found" -o ignore \
94	    jls -jj
95
96	atf_check -s exit:0 \
97	    jail -c name=j persist host.hostname=jail1
98
99	atf_check -s exit:0 -o inline:'""\n' \
100	    jls -jj meta
101	atf_check -s exit:0 -o inline:'""\n' \
102	    jls -jj env
103
104	atf_check -s exit:0 \
105	    jail -m name=j meta="$(jot 3 1 3)" env="$(jot 2 11 12)"
106
107	atf_check -s exit:0 -o inline:"1\n2\n3\n" \
108	    jls -jj meta
109	atf_check -s exit:0 -o inline:"11\n12\n" \
110	    jls -jj env
111}
112jail_add_cleanup()
113{
114	jail -r j
115	return 0
116}
117
118atf_test_case "jail_reset" "cleanup"
119jail_reset_head()
120{
121	atf_set descr 'Test that metadata can be reset to an empty string with jail(8)'
122	atf_set require.user root
123	atf_set execenv jail
124}
125jail_reset_body()
126{
127	setup
128
129	atf_check -s not-exit:0 -e match:"not found" -o ignore \
130	    jls -jj
131
132	atf_check -s exit:0 \
133	    jail -c name=j persist meta="123" env="456"
134
135	atf_check -s exit:0 -o inline:"123\n" \
136	    jls -jj meta
137	atf_check -s exit:0 -o inline:"456\n" \
138	    jls -jj env
139
140	atf_check -s exit:0 \
141	    jail -m name=j meta= env=
142
143	atf_check -s exit:0 -o inline:'""\n' \
144	    jls -jj meta
145	atf_check -s exit:0 -o inline:'""\n' \
146	    jls -jj env
147}
148jail_reset_cleanup()
149{
150	jail -r j
151	return 0
152}
153
154atf_test_case "jls_libxo_json" "cleanup"
155jls_libxo_json_head()
156{
157	atf_set descr 'Test that metadata can be read with jls(8) using libxo JSON'
158	atf_set require.user root
159	atf_set execenv jail
160}
161jls_libxo_json_body()
162{
163	setup
164
165	atf_check -s not-exit:0 -e match:"not found" -o ignore \
166	    jls -jj
167
168	atf_check -s exit:0 \
169	    jail -c name=j persist meta="a b c" env="1 2 3"
170
171	atf_check -s exit:0 -o inline:'{"__version": "2", "jail-information": {"jail": [{"name":"j","meta":"a b c"}]}}\n' \
172	    jls -jj --libxo json name meta
173	atf_check -s exit:0 -o inline:'{"__version": "2", "jail-information": {"jail": [{"env":"1 2 3"}]}}\n' \
174	    jls -jj --libxo json env
175}
176jls_libxo_json_cleanup()
177{
178	jail -r j
179	return 0
180}
181
182atf_test_case "flua_create" "cleanup"
183flua_create_head()
184{
185	atf_set descr 'Test that metadata can be set upon jail creation with flua'
186	atf_set require.user root
187	atf_set execenv jail
188}
189flua_create_body()
190{
191	setup
192
193	atf_check -s not-exit:0 -e match:"not found" -o ignore \
194	    jls -jj
195
196	atf_check -s exit:0 \
197	    /usr/libexec/flua -ljail -e 'jail.setparams("j", {["meta"]="t1 t2=v2", ["env"]="BAC", ["persist"]="true"}, jail.CREATE)'
198
199	atf_check -s exit:0 -o inline:"t1 t2=v2\n" \
200	    /usr/libexec/flua -ljail -e 'jid, res = jail.getparams("j", {"meta"}); print(res["meta"])'
201	atf_check -s exit:0 -o inline:"BAC\n" \
202	    /usr/libexec/flua -ljail -e 'jid, res = jail.getparams("j", {"env"}); print(res["env"])'
203}
204flua_create_cleanup()
205{
206	jail -r j
207	return 0
208}
209
210atf_test_case "flua_modify" "cleanup"
211flua_modify_head()
212{
213	atf_set descr 'Test that metadata can be changed with flua after jail creation'
214	atf_set require.user root
215	atf_set execenv jail
216}
217flua_modify_body()
218{
219	setup
220
221	atf_check -s not-exit:0 -e match:"not found" -o ignore \
222	    jls -jj
223
224	atf_check -s exit:0 \
225	    jail -c name=j persist meta="ABC" env="123"
226
227	atf_check -s exit:0 -o inline:"ABC\n" \
228	    jls -jj meta
229	atf_check -s exit:0 -o inline:"123\n" \
230	    jls -jj env
231
232	atf_check -s exit:0 \
233	    /usr/libexec/flua -ljail -e 'jail.setparams("j", {["meta"]="t1 t2=v", ["env"]="4"}, jail.UPDATE)'
234
235	atf_check -s exit:0 -o inline:"t1 t2=v\n" \
236	    jls -jj meta
237	atf_check -s exit:0 -o inline:"4\n" \
238	    jls -jj env
239}
240flua_modify_cleanup()
241{
242	jail -r j
243	return 0
244}
245
246atf_test_case "env_readable_by_jail" "cleanup"
247env_readable_by_jail_head()
248{
249	atf_set descr 'Test that a jail can read its own env parameter via sysctl(8)'
250	atf_set require.user root
251	atf_set execenv jail
252}
253env_readable_by_jail_body()
254{
255	setup
256
257	atf_check -s not-exit:0 -e match:"not found" -o ignore \
258	    jls -jj
259
260	atf_check -s exit:0 \
261	    jail -c name=j persist meta="a b c" env="CBA"
262
263	atf_check -s exit:0 -o inline:"a b c\n" \
264	    jls -jj meta
265	atf_check -s exit:0 -o inline:"CBA\n" \
266	    jls -jj env
267
268	atf_check -s exit:0 -o inline:"CBA\n" \
269	    jexec j sysctl -n security.jail.env
270}
271env_readable_by_jail_cleanup()
272{
273	jail -r j
274	return 0
275}
276
277atf_test_case "not_inheritable" "cleanup"
278not_inheritable_head()
279{
280	atf_set descr 'Test that a jail does not inherit metadata from its parent jail'
281	atf_set require.user root
282	atf_set execenv jail
283}
284not_inheritable_body()
285{
286	setup
287
288	atf_check -s not-exit:0 -e match:"not found" -o ignore \
289	    jls -j parent
290
291	atf_check -s exit:0 \
292	    jail -c name=parent children.max=1 persist meta="abc" env="cba"
293
294	jexec parent jail -c name=child persist
295
296	atf_check -s exit:0 -o inline:"abc\n" \
297	    jls -j parent meta
298	atf_check -s exit:0 -o inline:'""\n' \
299	    jls -j parent.child meta
300
301	atf_check -s exit:0 -o inline:"cba\n" \
302	    jexec parent sysctl -n security.jail.env
303	atf_check -s exit:0 -o inline:"\n" \
304	    jexec parent.child sysctl -n security.jail.env
305}
306not_inheritable_cleanup()
307{
308	jail -r parent.child
309	jail -r parent
310	return 0
311}
312
313atf_test_case "maxbufsize" "cleanup"
314maxbufsize_head()
315{
316	atf_set descr 'Test that metadata buffer maximum size can be changed'
317	atf_set require.user root
318	atf_set is.exclusive true
319}
320maxbufsize_body()
321{
322	setup
323
324	jn=jailmeta_maxbufsize
325
326	atf_check -s not-exit:0 -e match:"not found" -o ignore \
327	    jls -j $jn
328
329	# the size counts string length and the trailing \0 char
330	origmax=$(sysctl -n security.jail.meta_maxbufsize)
331
332	# must be fine with current max
333	atf_check -s exit:0 \
334	    jail -c name=$jn persist meta="$(printf %$((origmax-1))s)"
335	atf_check -s exit:0 -o inline:"${origmax}\n" \
336	    jls -j $jn meta | wc -c
337	#
338	atf_check -s exit:0 \
339	    jail -m name=$jn env="$(printf %$((origmax-1))s)"
340	atf_check -s exit:0 -o inline:"${origmax}\n" \
341	    jls -j $jn env | wc -c
342
343	# should not allow exceeding current max
344	atf_check -s not-exit:0 -e match:"too large" \
345	    jail -m name=$jn meta="$(printf %${origmax}s)"
346	#
347	atf_check -s not-exit:0 -e match:"too large" \
348	    jail -m name=$jn env="$(printf %${origmax}s)"
349
350	# should allow the same size with increased max
351	newmax=$((origmax + 1))
352	sysctl security.jail.meta_maxbufsize=$newmax
353	atf_check -s exit:0 \
354	    jail -m name=$jn meta="$(printf %${origmax}s)"
355	atf_check -s exit:0 -o inline:"${origmax}\n" \
356	    jls -j $jn meta | wc -c
357	#
358	atf_check -s exit:0 \
359	    jail -m name=$jn env="$(printf %${origmax}s)"
360	atf_check -s exit:0 -o inline:"${origmax}\n" \
361	    jls -j $jn env | wc -c
362
363	# decrease back to the original max
364	sysctl security.jail.meta_maxbufsize=$origmax
365	atf_check -s not-exit:0 -e match:"too large" \
366	    jail -m name=$jn meta="$(printf %${origmax}s)"
367	#
368	atf_check -s not-exit:0 -e match:"too large" \
369	    jail -m name=$jn env="$(printf %${origmax}s)"
370
371	# the previously set long meta is still readable as is
372	# due to the soft limit remains higher than the hard limit
373	atf_check_equal '${newmax}' '$(sysctl -n security.jail.param.meta)'
374	atf_check_equal '${newmax}' '$(sysctl -n security.jail.param.env)'
375	atf_check -s exit:0 -o inline:"${origmax}\n" \
376	    jls -j $jn meta | wc -c
377	#
378	atf_check -s exit:0 -o inline:"${origmax}\n" \
379	    jls -j $jn env | wc -c
380}
381maxbufsize_cleanup()
382{
383	jail -r jailmeta_maxbufsize
384	return 0
385}
386
387atf_test_case "keyvalue" "cleanup"
388keyvalue_head()
389{
390	atf_set descr 'Test that metadata can be handled as a set of key=value\n strings using jail(8), jls(8), and flua'
391	atf_set require.user root
392	atf_set execenv jail
393}
394keyvalue_generic()
395{
396	local meta=$1
397
398	atf_check -sexit:0 -oinline:'""\n'		jls -jj $meta
399
400	# Note: each sub-case depends on the results of the previous ones
401
402	# Should be able to extract a key added manually
403	atf_check -sexit:0				jail -m name=j $meta="a=1"
404	atf_check -sexit:0 -oinline:'a=1\n'		jls -jj $meta
405	atf_check -sexit:0 -oinline:'1\n'		jls -jj $meta.a
406	atf_check -sexit:0				jail -m name=j $meta="$(printf 'a=2\nb=3')"
407	atf_check -sexit:0 -oinline:'a=2\nb=3\n'	jls -jj $meta
408	atf_check -sexit:0 -oinline:'2\n'		jls -jj $meta.a
409	atf_check -sexit:0 -oinline:'3\n'		jls -jj $meta.b
410
411	# Should provide nothing for a non-found key
412	atf_check -sexit:0 -oinline:'\n'		jls -jj $meta.c
413
414	# Should be able to lookup multiple keys at once
415	atf_check -sexit:0 -oinline:'3 2\n'		jls -jj $meta.b $meta.a
416
417	# Should be able to lookup keys and the whole buffer at once
418	atf_check -sexit:0 -oinline:'3 a=2\nb=3 2\n'	jls -jj $meta.b $meta $meta.a
419
420	# Should be able to lookup a key using libxo-based JSON output
421	s='{"__version": "2", "jail-information": {"jail": [{"'$meta'.b":"3"}]}}\n'
422	atf_check -s exit:0 -o inline:"$s"		jls -jj --libxo json $meta.b
423
424	# Should provide nothing for a non-found key using libxo-based JSON output
425	s='{"__version": "2", "jail-information": {"jail": [{}]}}\n'
426	atf_check -s exit:0 -o inline:"$s"		jls -jj --libxo json $meta.c $meta.d
427
428	# Should be able to lookup a key using flua
429	atf_check -s exit:0 -o inline:"2\n"	\
430	    /usr/libexec/flua -ljail -e 'jid, res = jail.getparams("j", {"'$meta'.a"}); print(res["'$meta'.a"])'
431
432	# Should provide nil for a non-found key using flua
433	atf_check -s exit:0 -o inline:"true\n"	\
434	    /usr/libexec/flua -ljail -e 'jid, res = jail.getparams("j", {"'$meta'.meta"}); print(res["'$meta'.meta"] == nil)'
435
436	# Should allow resetting a buffer
437	atf_check -sexit:0				jail -m name=j $meta=
438	atf_check -sexit:0 -oinline:' "" \n'		jls -jj $meta.c $meta $meta.a
439
440	# Should allow adding a new key
441	atf_check -sexit:0				jail -m name=j $meta.a=1
442	atf_check -sexit:0 -oinline:'1\n'		jls -jj $meta.a
443	atf_check -sexit:0 -oinline:'a=1\n'		jls -jj $meta
444
445	# Should allow adding multiple new keys at once
446	atf_check -sexit:0				jail -m name=j $meta.c=3 $meta.b=2
447	atf_check -sexit:0 -oinline:'3\n'		jls -jj $meta.c
448	atf_check -sexit:0 -oinline:'2\n'		jls -jj $meta.b
449	atf_check -sexit:0 -oinline:'b=2\nc=3\na=1\n'	jls -jj $meta
450
451	# Should replace existing keys
452	atf_check -sexit:0				jail -m name=j $meta.a=A $meta.c=C
453	atf_check -sexit:0 -oinline:'A\n'		jls -jj $meta.a
454	atf_check -sexit:0 -oinline:'C\n'		jls -jj $meta.c
455	atf_check -sexit:0 -oinline:'c=C\na=A\nb=2\n'	jls -jj $meta
456
457	# Should treat empty value correctly
458	atf_check -sexit:0				jail -m name=j $meta.a=
459	atf_check -sexit:0 -oinline:'""\n'		jls -jj $meta.a
460	atf_check -sexit:0 -oinline:'a=\nc=C\nb=2\n'	jls -jj $meta
461
462	# Should treat NULL value as a key removal
463	atf_check -sexit:0 -oinline:'2\n'		jls -jj $meta.b
464	atf_check -sexit:0				jail -m name=j $meta.b
465	atf_check -sexit:0 -oinline:'\n'		jls -jj $meta.b
466	atf_check -sexit:0 -oinline:'a=\nc=C\n'		jls -jj $meta
467
468	# Should allow changing the whole buffer and per key at once (order matters)
469	atf_check -sexit:0				jail -m name=j $meta.a=1 $meta=ttt $meta.b=2
470	atf_check -sexit:0 -oinline:'\n'		jls -jj $meta.a
471	atf_check -sexit:0 -oinline:'2\n'		jls -jj $meta.b
472	atf_check -sexit:0 -oinline:'b=2\nttt\n'	jls -jj $meta
473
474	# Should treat only the first equal sign as syntax
475	atf_check -sexit:0				jail -m name=j $meta.b==
476	atf_check -sexit:0 -oinline:'=\n'		jls -jj $meta.b
477	atf_check -sexit:0 -oinline:'b==\nttt\n'	jls -jj $meta
478
479	# Should allow adding or modifying keys using flua
480	atf_check -s exit:0 \
481	    /usr/libexec/flua -ljail -e 'jail.setparams("j", {["'$meta.b'"]="ttt", ["'$meta'.c"]="C"}, jail.UPDATE)'
482	atf_check -sexit:0 -oinline:'ttt\n'		jls -jj $meta.b
483	atf_check -sexit:0 -oinline:'C\n'		jls -jj $meta.c
484
485	# Should allow key removal using flua
486	atf_check -s exit:0 \
487	    /usr/libexec/flua -ljail -e 'jail.setparams("j", {["'$meta.c'"] = {}}, jail.UPDATE)'
488	atf_check -sexit:0 -oinline:'\n'		jls -jj $meta.c
489	atf_check -s exit:0 \
490	    /usr/libexec/flua -ljail -e 'jail.setparams("j", {["'$meta.b'"] = false}, jail.UPDATE)'
491	atf_check -sexit:0 -oinline:'\n'		jls -jj $meta.b
492
493	# Should respectively support "jls -s" for a missing key
494	atf_check -sexit:0 -oinline:''$meta'.missing\n'	jls -jj -s $meta.missing
495}
496keyvalue_body()
497{
498	setup
499
500	atf_check -s exit:0 \
501	    jail -c name=j persist meta env
502
503	keyvalue_generic "meta"
504	keyvalue_generic "env"
505}
506keyvalue_cleanup()
507{
508	jail -r j
509	return 0
510}
511
512atf_test_case "keyvalue_contention" "cleanup"
513keyvalue_contention_head()
514{
515	atf_set descr 'Try to stress metadata read/write mechanism with some contention'
516	atf_set require.user root
517	atf_set execenv jail
518	atf_set timeout 30
519}
520keyvalue_stresser()
521{
522	local jailname=$1
523	local modifier=$2
524
525	while true
526	do
527		jail -m name=$jailname $modifier
528	done
529}
530keyvalue_contention_body()
531{
532	setup
533
534	atf_check -s exit:0 jail -c name=j persist meta env
535
536	keyvalue_stresser "j" "meta.a=1" &
537	apid=$!
538	keyvalue_stresser "j" "meta.b=2" &
539	bpid=$!
540	keyvalue_stresser "j" "env.c=3" &
541	cpid=$!
542	keyvalue_stresser "j" "env.d=4" &
543	dpid=$!
544
545	for it in $(jot 8)
546	do
547		jail -m name=j meta='meta=META' env='env=ENV'
548		sleep 1
549		atf_check -sexit:0 -oinline:'1\n'	jls -jj meta.a
550		atf_check -sexit:0 -oinline:'2\n'	jls -jj meta.b
551		atf_check -sexit:0 -oinline:'3\n'	jls -jj env.c
552		atf_check -sexit:0 -oinline:'4\n'	jls -jj env.d
553		atf_check -sexit:0 -oinline:'META\n'	jls -jj meta.meta
554		atf_check -sexit:0 -oinline:'ENV\n'	jls -jj env.env
555	done
556
557	# TODO: Think of adding a stresser on the kernel side which does
558	#       osd_set() w/o allprison lock. It could test the compare
559	#       and swap mechanism in jm_osd_method_set().
560
561	kill -9 $apid $bpid $cpid $dpid
562}
563keyvalue_contention_cleanup()
564{
565	jail -r j
566	return 0
567}
568
569atf_init_test_cases()
570{
571	atf_add_test_case "jail_create"
572	atf_add_test_case "jail_modify"
573	atf_add_test_case "jail_add"
574	atf_add_test_case "jail_reset"
575
576	atf_add_test_case "jls_libxo_json"
577
578	atf_add_test_case "flua_create"
579	atf_add_test_case "flua_modify"
580
581	atf_add_test_case "env_readable_by_jail"
582	atf_add_test_case "not_inheritable"
583
584	atf_add_test_case "maxbufsize"
585
586	atf_add_test_case "keyvalue"
587	atf_add_test_case "keyvalue_contention"
588}
589