syscalls/prctl04.c: New test for prctl() with PR_{SET, GET}_SECCOMP
PATCH 1/1 syscalls/prctl04.c: New test for prctl() with PR_{SET, GET}_SECCOMP
--- configure.ac | 1 + include/lapi/prctl.h | 5 + include/lapi/seccomp.h | 36 +++ runtest/syscalls | 1 + testcases/kernel/syscalls/prctl/.gitignore | 1 + testcases/kernel/syscalls/prctl/prctl04.c | 262 +++++++++++++++++++++ 6 files changed, 306 insertions(+) create mode 100644 include/lapi/seccomp.h create mode 100644 testcases/kernel/syscalls/prctl/prctl04.c
configure.ac +1 -0
@@ -46,6 +46,7 @@ AC_CHECK_HEADERS([ \ linux/mempolicy.h \ linux/module.h \ linux/netlink.h \ + linux/seccomp.h \ linux/userfaultfd.h \ mm.h \ netinet/sctp.h \
include/lapi/prctl.h +5 -0
@@ -14,4 +14,9 @@# define PR_GET_CHILD_SUBREAPER 37#endif +#ifndef PR_SET_SECCOMP +# define PR_GET_SECCOMP 21 +# define PR_SET_SECCOMP 22 +#endif +#endif /* LAPI_PRCTL_H__ */
include/lapi/seccomp.h new file +36 -0
@@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* +* Copyright (c) 2019 FUJITSU LIMITED. All rights reserved. +* Author: Yang Xu <[email protected]> +*/ +#ifndef LAPI_SECCOMP_H__ +# define _LAPI_SECCOMP_H + +#include <linux/types.h> + +/* Valid values for seccomp.mode and prctl(PR_SET_SECCOMP, <mode>) */ +#define SECCOMP_MODE_DISABLED 0 +#define SECCOMP_MODE_STRICT 1 +#define SECCOMP_MODE_FILTER 2 + +#define SECCOMP_RET_KILL_THREAD 0x00000000U/* kill the thread */ +#define SECCOMP_RET_KILL SECCOMP_RET_KILL_THREAD +#define SECCOMP_RET_ALLOW 0x7fff0000U/* allow */ + +/** +* struct seccomp_data - the format the BPF program executes over. +* @nr: the system call number +* @arch: indicates system call convention as an AUDIT_ARCH_* value +* as defined in <linux/audit.h>. +* @instruction_pointer: at the time of the system call. +* @args: up to 6 system call arguments always stored as 64-bit values +* regardless of the architecture. +*/ +struct seccomp_data { +int nr; +__u32 arch; +__u64 instruction_pointer; +__u64 args[6 ]; +}; + +#endif /* _LAPI_SECCOMP_H */
runtest/syscalls +1 -0
@@ -863,6 +863,7 @@ ppoll01 ppoll01 prctl01 prctl01 prctl02 prctl02 prctl03 prctl03 +prctl04 prctl04 pread01 pread01 pread01_64 pread01_64
testcases/kernel/syscalls/prctl/.gitignore +1 -0
@@ -1,3 +1,4 @@ /prctl01 /prctl02 /prctl03 +/prctl04
testcases/kernel/syscalls/prctl/prctl04.c new file +262 -0
262 lines not on this page · raw message
Signature · 1 line
2.18.1
PATCH 1/1 syscalls/prctl04.c: New test for prctl() with PR_{SET, GET}_SECCOMP
Hi!
Quoted 123 lines
configure.ac +1
-0@@ -46,6 +46,7 @@ AC_CHECK_HEADERS([ \ linux/mempolicy.h \ linux/module.h \ linux/netlink.h \ + linux/seccomp.h \ linux/userfaultfd.h \ mm.h \ netinet/sctp.h \include/lapi/prctl.h +5
-0@@ -14,4 +14,9 @@# define PR_GET_CHILD_SUBREAPER 37#endif +#ifndef PR_SET_SECCOMP +# define PR_GET_SECCOMP 21 +# define PR_SET_SECCOMP 22 +#endif +#endif /* LAPI_PRCTL_H__ */ include/lapi/seccomp.h new file +36
-0@@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* +* Copyright (c) 2019 FUJITSU LIMITED. All rights reserved. +* Author: Yang Xu <[email protected]> +*/ +#ifndef LAPI_SECCOMP_H__ +# define _LAPI_SECCOMP_H + +#include <linux/types.h> + +/* Valid values for seccomp.mode and prctl(PR_SET_SECCOMP, <mode>) */ +#define SECCOMP_MODE_DISABLED 0 +#define SECCOMP_MODE_STRICT 1 +#define SECCOMP_MODE_FILTER 2 + +#define SECCOMP_RET_KILL_THREAD 0x00000000U/* kill the thread */ +#define SECCOMP_RET_KILL SECCOMP_RET_KILL_THREAD +#define SECCOMP_RET_ALLOW 0x7fff0000U/* allow */ + +/** +* struct seccomp_data - the format the BPF program executes over. +* @nr: the system call number +* @arch: indicates system call convention as an AUDIT_ARCH_* value +* as defined in <linux/audit.h>. +* @instruction_pointer: at the time of the system call. +* @args: up to 6 system call arguments always stored as 64-bit values +* regardless of the architecture. +*/ +struct seccomp_data { +int nr; +__u32 arch; +__u64 instruction_pointer; +__u64 args[6 ]; +}; + +#endif /* _LAPI_SECCOMP_H */ runtest/syscalls +1
-0@@ -863,6 +863,7 @@ ppoll01 ppoll01 prctl01 prctl01 prctl02 prctl02 prctl03 prctl03 +prctl04 prctl04 pread01 pread01 pread01_64 pread01_64testcases/kernel/syscalls/prctl/.gitignore +1
-0@@ -1,3 +1,4 @@ /prctl01 /prctl02 /prctl03 +/prctl04testcases/kernel/syscalls/prctl/prctl04.c new file +28
-0@@ -0,0 +1,262 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* +* Copyright (c) 2019 FUJITSU LIMITED. All rights reserved. +* Author: Yang Xu <[email protected]> +* +* Test PR_GET_SECCOMP and PR_SET_SECCOMP of prctl(2). +* 1) If PR_SET_SECCOMP sets the SECCOMP_MODE_STRICT for the calling thread, +* the only system call that the thread is permitted to make are read(2), +* write(2),_exit(2)(but not exit_group(2)), and sigreturn(2). Other +* system calls result in the delivery of a SIGKILL signal. This operation +* is available only if the kernel is configured with CONFIG_SECCOMP enabled. +* 2) If PR_SET_SECCOMP sets the SECCOMP_MODE_FILTER for the calling thread, +* the system calls allowed are defined by a pointer to a Berkeley Packet +* Filter. Other system calls result int the delivery of a SIGSYS signal +* with SECCOMP_RET_KILL. The SECCOMP_SET_MODE_FILTER operation is available +* only if the kernel is configured with CONFIG_SECCOMP_FILTER enabled. +* 3) If SECCOMP_MODE_FILTER filters permit fork(2), then the seccomp mode +* is inherited by children created by fork(2). +*/ + +#include <errno.h> +#include <signal.h> +#include <sys/prctl.h> +#include <sys/wait.h> +#include <sys/types.h> +#include <linux/filter.h> +#include <unistd.h> +#include <stdlib.h>
testcases/kernel/syscalls/prctl/prctl04.c line 38
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl04.c new file +10
-0+#include <stddef.h> +#include "tst_test.h" +#include "lapi/syscalls.h" +#include "lapi/prctl.h" +#include "config.h" +#ifdef HAVE_LINUX_SECCOMP_H +#include <linux/seccomp.h> +#else +#include <lapi/seccomp.h> +#endif
This ifdef should be in the lapi/seccomp.h instead.
I.e. the test should only include lapi/seccomp.h and should not care if linux/seccomp.h is present or not.
Quoted 49 lines
testcases/kernel/syscalls/prctl/prctl04.c new file +49
-0+#define FNAME "filename" + +static const struct sock_filter strict_filter[] = { +BPF_STMT (BPF_LD |BPF_W |BPF_ABS , (offsetof (struct seccomp_data , nr))), + +BPF_JUMP (BPF_JMP |BPF_JEQ , __NR_close,5 ,0 ), +BPF_JUMP (BPF_JMP |BPF_JEQ , __NR_exit,4 ,0 ), +BPF_JUMP (BPF_JMP |BPF_JEQ , __NR_wait4,3 ,0 ), +BPF_JUMP (BPF_JMP |BPF_JEQ , __NR_write,2 ,0 ), +BPF_JUMP (BPF_JMP |BPF_JEQ , __NR_clone,1 ,0 ), + +BPF_STMT (BPF_RET |BPF_K ,SECCOMP_RET_KILL ), +BPF_STMT (BPF_RET |BPF_K ,SECCOMP_RET_ALLOW ) +}; + +static const struct sock_fprog strict = { + .len = (unsigned short )ARRAY_SIZE (strict_filter), + .filter = (struct sock_filter *)strict_filter +}; + +static void check_strict_mode (int val) +{ +int fd; +char buf[2 ]; + + fd =SAFE_OPEN (FNAME ,O_RDWR |O_CREAT ,0666 ); + +TEST (prctl (PR_SET_SECCOMP ,SECCOMP_MODE_STRICT )); +if (TST_RET ==-1 ) { +if (TST_ERR ==EINVAL ) { +tst_res (TCONF , +"prctl(PR_SET_SECCOMP) doesn't support " +"SECCOMP_MODE_STRICT" ); + }else { +tst_res (TFAIL |TTERRNO , +"prctl(PR_SET_SECCOMP) sets " +"SECCOMP_MODE_STRICT failed" ); + } +return ; + } +if (val ==1 ) { +tst_res (TPASS , +"prctl(PR_SET_SECCOMP) sets SECCOMP_MODE_STRICT " +"succeed" ); +prctl (PR_GET_SECCOMP ); +tst_res (TFAIL ,"prctl(PR_GET_SECCOMP) succeed unexpectedly" ); + } +if (val ==2 ) { +SAFE_WRITE (1 , fd,"a" ,1 );
testcases/kernel/syscalls/prctl/prctl04.c line 98
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl04.c new file +10
-0+SAFE_READ (0 , fd, buf,1 ); +tst_res (TPASS , +"SECCOMP_MODE_STRICT permits read(2) write(2) " +"and _exit(2)" ); + } +if (val ==3 ) { +close (fd); +tst_res (TFAIL , +"SECCOMP_MODE_STRICT permits close(2) unexpectdly" ); + }
This is way too ugly, if nothing else we should use switch() here.
Quoted 41 lines
testcases/kernel/syscalls/prctl/prctl04.c new file +41
-0+tst_syscall (__NR_exit,0 ); +} + +static void check_filter_mode (int val) +{ +int childpid; +int childstatus; +int fd; + + fd =SAFE_OPEN (FNAME ,O_RDWR |O_CREAT ,0666 ); + +TEST (prctl (PR_SET_SECCOMP ,SECCOMP_MODE_FILTER , &strict)); +if (TST_RET ==-1 ) { +if (TST_ERR ==EFAULT ) +tst_res (TFAIL ,"the strict prog is an invalid address" ); +else +tst_res (TFAIL |TERRNO , +"prctl(PR_SET_SECCOMP) sets strict filter " +"failed" ); +return ; + } +if (val ==1 ) { +tst_res (TPASS , +"prctl(PR_SET_SECCOMP) sets strict filter succeed" ); +prctl (PR_GET_SECCOMP ); +tst_res (TFAIL ,"prctl(PR_GET_SECCOMP) succeed unexpectedly" ); + } +if (val ==2 ) { +close (fd); +tst_res (TPASS ,"SECCOMP_MODE_FILTER permits close(2)" ); + } +if (val ==3 ) +exit (0 ); +if (val ==4 ) { + childpid =fork (); +if (childpid ==0 ) { +tst_res (TPASS ,"SECCOMP_MODE_FILTER permits fork(2)" ); +exit (0 ); + }else { +wait4 (childpid, &childstatus,0 ,NULL ); + if (WIFSIGNALED(childstatus) &&
testcases/kernel/syscalls/prctl/prctl04.c line 150
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl04.c new file +10
-0+ WTERMSIG(childstatus) == SIGSYS) +tst_res (TPASS , +"SECCOMP_MODE_FILTER has been " +"inherited by child" ); + else + tst_res(TFAIL, +"SECCOMP_MODE_FILTER isn't been " + "inherited by child"); + } + }
Here as well, use switch().
Also it would make sense to put the more complicated bodies, for instance case val == 4 into a separate function.
Quoted 93 lines
testcases/kernel/syscalls/prctl/prctl04.c new file +93
-0+tst_syscall (__NR_exit,0 ); +} + +static void verify_prctl (void ) +{ +int pid; +int status; + +TEST (prctl (PR_GET_SECCOMP )); +if (TST_RET ==-1 ) { +if (TST_ERR ==EINVAL ) { +tst_res (TCONF , +"prctl() doesn't support PR_GET_SECCOMP" ); + }else { +tst_res (TFAIL |TTERRNO , +"prctl(PR_GET_SECCOMP) failed" ); + } +return ; + } +tst_res (TPASS ,"prctl(PR_GET_SECCOMP) succeed" ); + +/*call get_seccomp when in stric mode ,it should be killed*/ + pid =SAFE_FORK (); +if (pid ==0 ) { +check_strict_mode (1 ); + }else { +SAFE_WAITPID (pid, &status,0 ); +if (WIFSIGNALED (status) &&WTERMSIG (status) ==SIGKILL ) +tst_res (TPASS , +"SECCOMP_MODE_STRICT doesn't permit " +"GET_SECCOMP call" ); + } + +/*positive check in secure computing mode*/ + pid =SAFE_FORK (); +if (pid ==0 ) { +check_strict_mode (2 ); + }else { +SAFE_WAITPID (pid, &status,0 ); +if (WIFSIGNALED (status) &&WTERMSIG (status) ==SIGKILL ) +tst_res (TFAIL , +"SECCOMP_MODE_STRICT doesn't permit " +"read(2) write(2) and _exit(2)" ); + } + +/*negative check in secure computing mode*/ + pid =SAFE_FORK (); +if (pid ==0 ) { +check_strict_mode (3 ); + }else { +SAFE_WAITPID (pid, &status,0 ); +if (WIFSIGNALED (status) &&WTERMSIG (status) ==SIGKILL ) +tst_res (TPASS , +"SECCOMP_MODE_STRICT doesn't permit close(2)" ); + } + +/*call get_seccomp in filter mode should be killed by SIGSYS signal*/ + pid =SAFE_FORK (); +if (pid ==0 ) { +check_filter_mode (1 ); + }else { +SAFE_WAITPID (pid, &status,0 ); +if (WIFSIGNALED (status) &&WTERMSIG (status) ==SIGSYS ) +tst_res (TPASS , +"SECCOMP_MODE_FILTER doestn't permit " +"GET_SECCOMP call" ); + } + +/*positive check in SECCOMP_MODE_FILTER*/ + pid =SAFE_FORK (); +if (pid ==0 ) { +check_filter_mode (2 ); + }else { +SAFE_WAITPID (pid, &status,0 ); +if (WIFSIGNALED (status) &&WTERMSIG (status) ==SIGSYS ) +tst_res (TFAIL , +"SECCOMP_MODE_FILTER doesn't permit close(2)" ); + } + +/*negative check in SECCOMP_MODE_FILTER*/ + pid =SAFE_FORK (); +if (pid ==0 ) { +check_filter_mode (3 ); + }else { +SAFE_WAITPID (pid, &status,0 ); +if (WIFSIGNALED (status) &&WTERMSIG (status) ==SIGSYS ) +tst_res (TPASS , +"SECCOMP_MODE_FILTER doesn't permit exit()" ); +else +tst_res (TFAIL , +"SECCOMP_MODE_FILTER permits exit() " +"unexpectdly" ); + }
testcases/kernel/syscalls/prctl/prctl04.c line 253
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl04.c new file +10
-0+ + pid =SAFE_FORK (); +if (pid ==0 ) { +check_filter_mode (4 ); + }else { +SAFE_WAITPID (pid, &status,0 ); +if (WIFSIGNALED (status) &&WTERMSIG (status) ==SIGSYS ) +tst_res (TFAIL , +"SECCOMP_MODE_FILTER fork failed " +"unexpectdly" );
This is a minor, but multiline statements should be enclosed in a curly braces accodingly to LKML and also string constants shouldn't be split into multiple lines, so this should be:
if (WIFSIGNALED(status) && WTERMSIG(status) == SIGSYS) {
tst_res(TFAIL,
"SECCOMP_MODE_FILTER fork failed unexpectdly");
}Also the else branch is useless since the function check_filter_mode() calls exit.
So we can simply do:
pid = SAFE_FORK(); if (pid == 0) check_fitler_mode(4); SAFE_WAITPID(...);
testcases/kernel/syscalls/prctl/prctl04.c line 253
Quoted 2 lines
testcases/kernel/syscalls/prctl/prctl04.c new file +2
-0+ } +}
You are doing several different tests in this function, can we split this and use .test and .tcnt instead of .test_all?
testcases/kernel/syscalls/prctl/prctl04.c line 262
Quoted 6 lines
testcases/kernel/syscalls/prctl/prctl04.c new file +6
-0+static struct tst_test test = { + .test_all = verify_prctl, + .forks_child =1 , + .needs_tmpdir =1 , + .needs_root =1 , +};
Otherwise it looks good.
Signature · 2 lines
Cyril Hrubis [email protected]
PATCH v2 1/1 syscalls/prctl04.c: New test for prctl() with PR_{SET, GET}_SECCOMP
--- configure.ac | 1 + include/lapi/prctl.h | 5 + include/lapi/seccomp.h | 40 ++++ runtest/syscalls | 1 + testcases/kernel/syscalls/prctl/.gitignore | 1 + testcases/kernel/syscalls/prctl/prctl04.c | 229 +++++++++++++++++++++ 6 files changed, 277 insertions(+) create mode 100644 include/lapi/seccomp.h create mode 100644 testcases/kernel/syscalls/prctl/prctl04.c
configure.ac +1 -0
@@ -46,6 +46,7 @@ AC_CHECK_HEADERS([ \ linux/mempolicy.h \ linux/module.h \ linux/netlink.h \ + linux/seccomp.h \ linux/userfaultfd.h \ mm.h \ netinet/sctp.h \
include/lapi/prctl.h +5 -0
@@ -9,6 +9,11 @@#include <sys/prctl.h> +#ifndef PR_SET_SECCOMP +# define PR_GET_SECCOMP 21 +# define PR_SET_SECCOMP 22 +#endif +#ifndef PR_SET_CHILD_SUBREAPER # define PR_SET_CHILD_SUBREAPER 36# define PR_GET_CHILD_SUBREAPER 37
include/lapi/seccomp.h new file +40 -0
@@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* +* Copyright (c) 2019 FUJITSU LIMITED. All rights reserved. +* Author: Yang Xu <[email protected]> +*/ +#ifndef LAPI_SECCOMP_H__ +# define _LAPI_SECCOMP_H + +#include <linux/types.h> + +#ifdef HAVE_LINUX_SECCOMP_H +#include <linux/seccomp.h> +#else +/* Valid values for seccomp.mode and prctl(PR_SET_SECCOMP, <mode>) */ +#define SECCOMP_MODE_DISABLED 0 +#define SECCOMP_MODE_STRICT 1 +#define SECCOMP_MODE_FILTER 2 + +#define SECCOMP_RET_KILL_THREAD 0x00000000U/* kill the thread */ +#define SECCOMP_RET_KILL SECCOMP_RET_KILL_THREAD +#define SECCOMP_RET_ALLOW 0x7fff0000U/* allow */ + +/** +* struct seccomp_data - the format the BPF program executes over. +* @nr: the system call number +* @arch: indicates system call convention as an AUDIT_ARCH_* value +* as defined in <linux/audit.h>. +* @instruction_pointer: at the time of the system call. +* @args: up to 6 system call arguments always stored as 64-bit values +* regardless of the architecture. +*/ +struct seccomp_data { +int nr; +__u32 arch; +__u64 instruction_pointer; +__u64 args[6 ]; +}; + +#endif /* HAVE_LINUX_SECCOMP_H*/ +#endif /* _LAPI_SECCOMP_H */
runtest/syscalls +1 -0
@@ -863,6 +863,7 @@ ppoll01 ppoll01 prctl01 prctl01 prctl02 prctl02 prctl03 prctl03 +prctl04 prctl04 pread01 pread01 pread01_64 pread01_64
testcases/kernel/syscalls/prctl/.gitignore +1 -0
@@ -1,3 +1,4 @@ /prctl01 /prctl02 /prctl03 +/prctl04
testcases/kernel/syscalls/prctl/prctl04.c new file +229 -0
229 lines not on this page · raw message
Signature · 1 line
2.18.1
PATCH v2 1/1 syscalls/prctl04.c: New test for prctl() with PR_{SET, GET}_SECCOMP
Hi!
Quoted 36 lines
include/lapi/seccomp.h new file +30
-0@@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* +* Copyright (c) 2019 FUJITSU LIMITED. All rights reserved. +* Author: Yang Xu <[email protected]> +*/ +#ifndef LAPI_SECCOMP_H__ +# define _LAPI_SECCOMP_H + +#include <linux/types.h> + +#ifdef HAVE_LINUX_SECCOMP_H +#include <linux/seccomp.h> +#else +/* Valid values for seccomp.mode and prctl(PR_SET_SECCOMP, <mode>) */ +#define SECCOMP_MODE_DISABLED 0 +#define SECCOMP_MODE_STRICT 1 +#define SECCOMP_MODE_FILTER 2 + +#define SECCOMP_RET_KILL_THREAD 0x00000000U/* kill the thread */ +#define SECCOMP_RET_KILL SECCOMP_RET_KILL_THREAD +#define SECCOMP_RET_ALLOW 0x7fff0000U/* allow */ + +/** +* struct seccomp_data - the format the BPF program executes over. +* @nr: the system call number +* @arch: indicates system call convention as an AUDIT_ARCH_* value +* as defined in <linux/audit.h>. +* @instruction_pointer: at the time of the system call. +* @args: up to 6 system call arguments always stored as 64-bit values +* regardless of the architecture.
include/lapi/seccomp.h line 40
Quoted 10 lines
include/lapi/seccomp.h new file +10
-0+*/ +struct seccomp_data { +int nr; +__u32 arch; +__u64 instruction_pointer; +__u64 args[6 ]; +}; + +#endif /* HAVE_LINUX_SECCOMP_H*/ +#endif /* _LAPI_SECCOMP_H */
The ifdefs could be made a bit more readable by proper indentation as:
#ifndef FOO_H__
#define FOO_H__
# include <header.h>
# ifdef HAVE_BAR # include <bar.h> # else # define BAR1 # define BAR2
# endif #endif /* FOO_H__ */
But that is very minor.
Quoted 146 lines
runtest/syscalls +1
-0@@ -863,6 +863,7 @@ ppoll01 ppoll01 prctl01 prctl01 prctl02 prctl02 prctl03 prctl03 +prctl04 prctl04 pread01 pread01 pread01_64 pread01_64testcases/kernel/syscalls/prctl/.gitignore +1
-0@@ -1,3 +1,4 @@ /prctl01 /prctl02 /prctl03 +/prctl04testcases/kernel/syscalls/prctl/prctl04.c new file +119
-0@@ -0,0 +1,229 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* +* Copyright (c) 2019 FUJITSU LIMITED. All rights reserved. +* Author: Yang Xu <[email protected]> +* +* Test PR_GET_SECCOMP and PR_SET_SECCOMP of prctl(2). +* 1) If PR_SET_SECCOMP sets the SECCOMP_MODE_STRICT for the calling thread, +* the only system call that the thread is permitted to make are read(2), +* write(2),_exit(2)(but not exit_group(2)), and sigreturn(2). Other +* system calls result in the delivery of a SIGKILL signal. This operation +* is available only if the kernel is configured with CONFIG_SECCOMP enabled. +* 2) If PR_SET_SECCOMP sets the SECCOMP_MODE_FILTER for the calling thread, +* the system calls allowed are defined by a pointer to a Berkeley Packet +* Filter. Other system calls result int the delivery of a SIGSYS signal +* with SECCOMP_RET_KILL. The SECCOMP_SET_MODE_FILTER operation is available +* only if the kernel is configured with CONFIG_SECCOMP_FILTER enabled. +* 3) If SECCOMP_MODE_FILTER filters permit fork(2), then the seccomp mode +* is inherited by children created by fork(2). +*/ + +#include <errno.h> +#include <signal.h> +#include <sys/prctl.h> +#include <sys/wait.h> +#include <sys/types.h> +#include <linux/filter.h> +#include <unistd.h> +#include <stdlib.h> +#include <stddef.h> +#include "tst_test.h" +#include "lapi/syscalls.h" +#include "lapi/prctl.h" +#include "config.h" +#include "lapi/seccomp.h" + +#define FNAME "filename" + +static const struct sock_filter strict_filter[] = { +BPF_STMT (BPF_LD |BPF_W |BPF_ABS , (offsetof (struct seccomp_data , nr))), + +BPF_JUMP (BPF_JMP |BPF_JEQ , __NR_close,5 ,0 ), +BPF_JUMP (BPF_JMP |BPF_JEQ , __NR_exit,4 ,0 ), +BPF_JUMP (BPF_JMP |BPF_JEQ , __NR_wait4,3 ,0 ), +BPF_JUMP (BPF_JMP |BPF_JEQ , __NR_write,2 ,0 ), +BPF_JUMP (BPF_JMP |BPF_JEQ , __NR_clone,1 ,0 ), + +BPF_STMT (BPF_RET |BPF_K ,SECCOMP_RET_KILL ), +BPF_STMT (BPF_RET |BPF_K ,SECCOMP_RET_ALLOW ) +}; + +static const struct sock_fprog strict = { + .len = (unsigned short )ARRAY_SIZE (strict_filter), + .filter = (struct sock_filter *)strict_filter +}; + +static void check_strict_mode (int ); +static void check_filter_mode (int ); + +static struct tcase { +void (*func_check)(); +int pass_flag; +int val; +int exp_signal; +char *message; +} tcases[] = { + {check_strict_mode,1 ,1 ,SIGKILL , +"SECCOMP_MODE_STRICT doesn't permit GET_SECCOMP call" }, + + {check_strict_mode,0 ,2 ,SIGKILL , +"SECCOMP_MODE_STRICT doesn't permit read(2) write(2) and _exit(2)" }, + + {check_strict_mode,1 ,3 ,SIGKILL , +"SECCOMP_MODE_STRICT doesn't permit close(2)" }, + + {check_filter_mode,1 ,1 ,SIGSYS , +"SECCOMP_MODE_FILTER doestn't permit GET_SECCOMP call" }, + + {check_filter_mode,0 ,2 ,SIGSYS , +"SECCOMP_MODE_FILTER doesn't permit close(2)" }, + + {check_filter_mode,1 ,3 ,SIGSYS , +"SECCOMP_MODE_FILTER doesn't permit exit()" }, + + {check_filter_mode,0 ,4 ,SIGSYS , +"SECCOMP_MODE_FILTER doesn't permit exit()" } +}; + +static void check_filter_mode_inherit (void ) +{ +int childpid; +int childstatus; + + childpid =fork (); +if (childpid ==0 ) { +tst_res (TPASS ,"SECCOMP_MODE_FILTER permits fork(2)" ); +exit (0 ); + } + +wait4 (childpid, &childstatus,0 ,NULL ); +if (WIFSIGNALED (childstatus) &&WTERMSIG (childstatus) ==SIGSYS ) +tst_res (TPASS , +"SECCOMP_MODE_FILTER has been inherited by child" ); +else +tst_res (TFAIL , +"SECCOMP_MODE_FILTER isn't been inherited by child" ); +} + +static void check_strict_mode (int val) +{ +int fd; +char buf[2 ]; + + fd =SAFE_OPEN (FNAME ,O_RDWR |O_CREAT ,0666 ); + +TEST (prctl (PR_SET_SECCOMP ,SECCOMP_MODE_STRICT )); +if (TST_RET ==-1 ) { +tst_res (TFAIL |TTERRNO , +"prctl(PR_SET_SECCOMP) sets SECCOMP_MODE_STRICT failed" ); +return ;
testcases/kernel/syscalls/prctl/prctl04.c line 129
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl04.c new file +10
-0+ } + +switch (val) { +case 1 : { +tst_res (TPASS , +"prctl(PR_SET_SECCOMP) sets SECCOMP_MODE_STRICT succeed" ); +prctl (PR_GET_SECCOMP ); +tst_res (TFAIL ,"prctl(PR_GET_SECCOMP) succeed unexpectedly" ); +break ; + }
There is no need to enclose the case switches between curly braces unless you need to declare variables there.
Quoted 70 lines
testcases/kernel/syscalls/prctl/prctl04.c new file +70
-0+case 2 : { +SAFE_WRITE (1 , fd,"a" ,1 ); +SAFE_READ (0 , fd, buf,1 ); +tst_res (TPASS , +"SECCOMP_MODE_STRICT permits read(2) write(2) and _exit(2)" ); +break ; + } +case 3 : { +close (fd); +tst_res (TFAIL , +"SECCOMP_MODE_STRICT permits close(2) unexpectdly" ); +break ; + } + } + +tst_syscall (__NR_exit,0 ); +} + +static void check_filter_mode (int val) +{ +int fd; + + fd =SAFE_OPEN (FNAME ,O_RDWR |O_CREAT ,0666 ); + +TEST (prctl (PR_SET_SECCOMP ,SECCOMP_MODE_FILTER , &strict)); +if (TST_RET ==-1 ) { +if (TST_ERR ==EINVAL ) +tst_res (TCONF , +"kernel doesn't support SECCOMP_MODE_FILTER" ); +else +tst_res (TFAIL |TERRNO , +"prctl(PR_SET_SECCOMP) sets strict filter failed" ); +return ; + } + +switch (val) { +case 1 : { +tst_res (TPASS , +"prctl(PR_SET_SECCOMP) sets strict filter succeed" ); +prctl (PR_GET_SECCOMP ); +tst_res (TFAIL ,"prctl(PR_GET_SECCOMP) succeed unexpectedly" ); +break ; + } +case 2 : { +close (fd); +tst_res (TPASS ,"SECCOMP_MODE_FILTER permits close(2)" ); +break ; + } +case 3 : +exit (0 ); +break ; +case 4 : +check_filter_mode_inherit (); +break ; + } +tst_syscall (__NR_exit,0 ); +} + +static void verify_prctl (unsigned int n) +{ +int pid; +int status; +struct tcase *tc = &tcases[n]; + + pid =SAFE_FORK (); +if (pid ==0 ) { + tc->func_check (tc->val); + }else { +SAFE_WAITPID (pid, &status,0 ); +if (WIFSIGNALED (status) &&WTERMSIG (status) == tc->exp_signal) {
testcases/kernel/syscalls/prctl/prctl04.c line 209
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl04.c new file +10
-0+if (tc->pass_flag) +tst_res (TPASS ,"%s" , tc->message); +else +tst_res (TFAIL ,"%s" , tc->message); +return ; + } + +if (n ==5 ) +tst_res (TFAIL , +"SECCOMP_MODE_FILTER permits exit() unexpectdly" );
^ Typo
Depending on the value of n here is wrong, this code will unexpectedly fail to work if someone adds testcases to the tcases array.
You can at least reuse the pass_flag and set it to 2 for this case, then you can do if (tc->pass_flag == 2) here instead.
testcases/kernel/syscalls/prctl/prctl04.c line 219
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl04.c new file +10
-0+ } +} + +static void setup (void ) +{ +TEST (prctl (PR_GET_SECCOMP )); +if (TST_RET ==0 ) +tst_res (TINFO ,"kernel support PR_GET/SET_SECCOMP" ); +if (TST_ERR ==EINVAL ) +tst_brk (TBROK ,"kernel doesn't support PR_GET/SET_SECCOMP" );
^
Shouldn't this be TCONF?
Also we should handle other error cases, so maybe:
if (TST_RET == 0) {
tst_res(TINFO, ...);
return;
}
if (TST_ERR == EINVAL)
tst_brk(TCONF, ...);
tst_brk(TBROK | TTERRNO, ...);testcases/kernel/syscalls/prctl/prctl04.c line 229
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl04.c new file +10
-0+} + +static struct tst_test test = { + .setup = setup, + .test = verify_prctl, + .tcnt =ARRAY_SIZE (tcases), + .forks_child =1 , + .needs_tmpdir =1 , + .needs_root =1 , +};
Other than these minor nits the test looks good to me.
Signature · 2 lines
Cyril Hrubis [email protected]
PATCH v2 1/1 syscalls/prctl04.c: New test for prctl() with PR_{SET, GET}_SECCOMP
Hi cyril
I will send a v3 patch for this. Thanks for your review.
Kind Regards
Yang Xu
Quoted 350 lines
Hi!
Quoted 36 lines
include/lapi/seccomp.h new file +30
-0@@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* +* Copyright (c) 2019 FUJITSU LIMITED. All rights reserved. +* Author: Yang Xu<[email protected]> +*/ +#ifndef LAPI_SECCOMP_H__ +# define _LAPI_SECCOMP_H + +#include <linux/types.h> + +#ifdef HAVE_LINUX_SECCOMP_H +#include <linux/seccomp.h> +#else +/* Valid values for seccomp.mode and prctl(PR_SET_SECCOMP,<mode>) */ +#define SECCOMP_MODE_DISABLED 0 +#define SECCOMP_MODE_STRICT 1 +#define SECCOMP_MODE_FILTER 2 + +#define SECCOMP_RET_KILL_THREAD 0x00000000U/* kill the thread */ +#define SECCOMP_RET_KILL SECCOMP_RET_KILL_THREAD +#define SECCOMP_RET_ALLOW 0x7fff0000U/* allow */ + +/** +* struct seccomp_data - the format the BPF program executes over. +* @nr: the system call number +* @arch: indicates system call convention as an AUDIT_ARCH_* value +* as defined in<linux/audit.h>. +* @instruction_pointer: at the time of the system call. +* @args: up to 6 system call arguments always stored as 64-bit values +* regardless of the architecture. include/lapi/seccomp.h line 40
Quoted 10 lines
include/lapi/seccomp.h new file +10
-0+*/ +struct seccomp_data { +int nr; +__u32 arch; +__u64 instruction_pointer; +__u64 args[6 ]; +}; + +#endif /* HAVE_LINUX_SECCOMP_H*/ +#endif /* _LAPI_SECCOMP_H */ The ifdefs could be made a bit more readable by proper indentation as:
#ifndef
FOO_H__
#defineFOO_H__# include<header.h>
# ifdef HAVE_BAR # include<bar.h> # else # define BAR1 # define BAR2# endif #endif /* FOO_H__ */But that is very minor.
Quoted 146 lines
runtest/syscalls +1
-0@@ -863,6 +863,7 @@ ppoll01 ppoll01 prctl01 prctl01 prctl02 prctl02 prctl03 prctl03 +prctl04 prctl04 pread01 pread01 pread01_64 pread01_64testcases/kernel/syscalls/prctl/.gitignore +1
-0@@ -1,3 +1,4 @@ /prctl01 /prctl02 /prctl03 +/prctl04testcases/kernel/syscalls/prctl/prctl04.c new file +119
-0@@ -0,0 +1,229 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* +* Copyright (c) 2019 FUJITSU LIMITED. All rights reserved. +* Author: Yang Xu<[email protected]> +* +* Test PR_GET_SECCOMP and PR_SET_SECCOMP of prctl(2). +* 1) If PR_SET_SECCOMP sets the SECCOMP_MODE_STRICT for the calling thread, +* the only system call that the thread is permitted to make are read(2), +* write(2),_exit(2)(but not exit_group(2)), and sigreturn(2). Other +* system calls result in the delivery of a SIGKILL signal. This operation +* is available only if the kernel is configured with CONFIG_SECCOMP enabled. +* 2) If PR_SET_SECCOMP sets the SECCOMP_MODE_FILTER for the calling thread, +* the system calls allowed are defined by a pointer to a Berkeley Packet +* Filter. Other system calls result int the delivery of a SIGSYS signal +* with SECCOMP_RET_KILL. The SECCOMP_SET_MODE_FILTER operation is available +* only if the kernel is configured with CONFIG_SECCOMP_FILTER enabled. +* 3) If SECCOMP_MODE_FILTER filters permit fork(2), then the seccomp mode +* is inherited by children created by fork(2). +*/ + +#include <errno.h> +#include <signal.h> +#include <sys/prctl.h> +#include <sys/wait.h> +#include <sys/types.h> +#include <linux/filter.h> +#include <unistd.h> +#include <stdlib.h> +#include <stddef.h> +#include "tst_test.h" +#include "lapi/syscalls.h" +#include "lapi/prctl.h" +#include "config.h" +#include "lapi/seccomp.h" + +#define FNAME "filename" + +static const struct sock_filter strict_filter[] = { +BPF_STMT (BPF_LD |BPF_W |BPF_ABS , (offsetof (struct seccomp_data , nr))), + +BPF_JUMP (BPF_JMP |BPF_JEQ , __NR_close,5 ,0 ), +BPF_JUMP (BPF_JMP |BPF_JEQ , __NR_exit,4 ,0 ), +BPF_JUMP (BPF_JMP |BPF_JEQ , __NR_wait4,3 ,0 ), +BPF_JUMP (BPF_JMP |BPF_JEQ , __NR_write,2 ,0 ), +BPF_JUMP (BPF_JMP |BPF_JEQ , __NR_clone,1 ,0 ), + +BPF_STMT (BPF_RET |BPF_K ,SECCOMP_RET_KILL ), +BPF_STMT (BPF_RET |BPF_K ,SECCOMP_RET_ALLOW ) +}; + +static const struct sock_fprog strict = { + .len = (unsigned short )ARRAY_SIZE (strict_filter), + .filter = (struct sock_filter *)strict_filter +}; + +static void check_strict_mode (int ); +static void check_filter_mode (int ); + +static struct tcase { +void (*func_check)(); +int pass_flag; +int val; +int exp_signal; +char *message; +} tcases[] = { + {check_strict_mode,1 ,1 ,SIGKILL , +"SECCOMP_MODE_STRICT doesn't permit GET_SECCOMP call" }, + + {check_strict_mode,0 ,2 ,SIGKILL , +"SECCOMP_MODE_STRICT doesn't permit read(2) write(2) and _exit(2)" }, + + {check_strict_mode,1 ,3 ,SIGKILL , +"SECCOMP_MODE_STRICT doesn't permit close(2)" }, + + {check_filter_mode,1 ,1 ,SIGSYS , +"SECCOMP_MODE_FILTER doestn't permit GET_SECCOMP call" }, + + {check_filter_mode,0 ,2 ,SIGSYS , +"SECCOMP_MODE_FILTER doesn't permit close(2)" }, + + {check_filter_mode,1 ,3 ,SIGSYS , +"SECCOMP_MODE_FILTER doesn't permit exit()" }, + + {check_filter_mode,0 ,4 ,SIGSYS , +"SECCOMP_MODE_FILTER doesn't permit exit()" } +}; + +static void check_filter_mode_inherit (void ) +{ +int childpid; +int childstatus; + + childpid =fork (); +if (childpid ==0 ) { +tst_res (TPASS ,"SECCOMP_MODE_FILTER permits fork(2)" ); +exit (0 ); + } + +wait4 (childpid,&childstatus,0 ,NULL ); +if (WIFSIGNALED (childstatus)&&WTERMSIG (childstatus) ==SIGSYS ) +tst_res (TPASS , +"SECCOMP_MODE_FILTER has been inherited by child" ); +else +tst_res (TFAIL , +"SECCOMP_MODE_FILTER isn't been inherited by child" ); +} + +static void check_strict_mode (int val) +{ +int fd; +char buf[2 ]; + + fd =SAFE_OPEN (FNAME ,O_RDWR |O_CREAT ,0666 ); + +TEST (prctl (PR_SET_SECCOMP ,SECCOMP_MODE_STRICT )); +if (TST_RET ==-1 ) { +tst_res (TFAIL |TTERRNO , +"prctl(PR_SET_SECCOMP) sets SECCOMP_MODE_STRICT failed" ); +return ;testcases/kernel/syscalls/prctl/prctl04.c line 129
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl04.c new file +10
-0+ } + +switch (val) { +case 1 : { +tst_res (TPASS , +"prctl(PR_SET_SECCOMP) sets SECCOMP_MODE_STRICT succeed" ); +prctl (PR_GET_SECCOMP ); +tst_res (TFAIL ,"prctl(PR_GET_SECCOMP) succeed unexpectedly" ); +break ; + }There is no need to enclose the case switches between curly braces unless you need to declare variables there.
Quoted 70 lines
testcases/kernel/syscalls/prctl/prctl04.c new file +70
-0+case 2 : { +SAFE_WRITE (1 , fd,"a" ,1 ); +SAFE_READ (0 , fd, buf,1 ); +tst_res (TPASS , +"SECCOMP_MODE_STRICT permits read(2) write(2) and _exit(2)" ); +break ; + } +case 3 : { +close (fd); +tst_res (TFAIL , +"SECCOMP_MODE_STRICT permits close(2) unexpectdly" ); +break ; + } + } + +tst_syscall (__NR_exit,0 ); +} + +static void check_filter_mode (int val) +{ +int fd; + + fd =SAFE_OPEN (FNAME ,O_RDWR |O_CREAT ,0666 ); + +TEST (prctl (PR_SET_SECCOMP ,SECCOMP_MODE_FILTER ,&strict)); +if (TST_RET ==-1 ) { +if (TST_ERR ==EINVAL ) +tst_res (TCONF , +"kernel doesn't support SECCOMP_MODE_FILTER" ); +else +tst_res (TFAIL |TERRNO , +"prctl(PR_SET_SECCOMP) sets strict filter failed" ); +return ; + } + +switch (val) { +case 1 : { +tst_res (TPASS , +"prctl(PR_SET_SECCOMP) sets strict filter succeed" ); +prctl (PR_GET_SECCOMP ); +tst_res (TFAIL ,"prctl(PR_GET_SECCOMP) succeed unexpectedly" ); +break ; + } +case 2 : { +close (fd); +tst_res (TPASS ,"SECCOMP_MODE_FILTER permits close(2)" ); +break ; + } +case 3 : +exit (0 ); +break ; +case 4 : +check_filter_mode_inherit (); +break ; + } +tst_syscall (__NR_exit,0 ); +} + +static void verify_prctl (unsigned int n) +{ +int pid; +int status; +struct tcase *tc =&tcases[n]; + + pid =SAFE_FORK (); +if (pid ==0 ) { + tc->func_check (tc->val); + }else { +SAFE_WAITPID (pid,&status,0 ); +if (WIFSIGNALED (status)&&WTERMSIG (status) == tc->exp_signal) {testcases/kernel/syscalls/prctl/prctl04.c line 209
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl04.c new file +10
-0+if (tc->pass_flag) +tst_res (TPASS ,"%s" , tc->message); +else +tst_res (TFAIL ,"%s" , tc->message); +return ; + } + +if (n ==5 ) +tst_res (TFAIL , +"SECCOMP_MODE_FILTER permits exit() unexpectdly" );^ TypoDepending on the value of n here is wrong, this code will unexpectedly fail to work if someone adds testcases to the tcases array.
You can at least reuse the
pass_flagand set it to 2 for this case, then you can do if (tc->pass_flag== 2) here instead.testcases/kernel/syscalls/prctl/prctl04.c line 219
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl04.c new file +10
-0+ } +} + +static void setup (void ) +{ +TEST (prctl (PR_GET_SECCOMP )); +if (TST_RET ==0 ) +tst_res (TINFO ,"kernel support PR_GET/SET_SECCOMP" ); +if (TST_ERR ==EINVAL ) +tst_brk (TBROK ,"kernel doesn't support PR_GET/SET_SECCOMP" );^
Shouldn't this be TCONF?Also we should handle other error cases, so maybe:
if (TST_RET == 0) { tst_res(TINFO, ...); return; } if (TST_ERR == EINVAL) tst_brk(TCONF, ...); tst_brk(TBROK | TTERRNO, ...);testcases/kernel/syscalls/prctl/prctl04.c line 229
Quoted 10 lines
testcases/kernel/syscalls/prctl/prctl04.c new file +10
-0+} + +static struct tst_test test = { + .setup = setup, + .test = verify_prctl, + .tcnt =ARRAY_SIZE (tcases), + .forks_child =1 , + .needs_tmpdir =1 , + .needs_root =1 , +};Other than these minor nits the test looks good to me.
PATCH v3 1/1 syscalls/prctl04.c: New test for prctl() with PR_{SET, GET}_SECCOMP
In reply to Cyril Hrubis--- configure.ac | 1 + include/lapi/prctl.h | 5 + include/lapi/seccomp.h | 40 ++++ runtest/syscalls | 1 + testcases/kernel/syscalls/prctl/.gitignore | 1 + testcases/kernel/syscalls/prctl/prctl04.c | 231 +++++++++++++++++++++ 6 files changed, 279 insertions(+) create mode 100644 include/lapi/seccomp.h create mode 100644 testcases/kernel/syscalls/prctl/prctl04.c
configure.ac +1 -0
@@ -46,6 +46,7 @@ AC_CHECK_HEADERS([ \ linux/mempolicy.h \ linux/module.h \ linux/netlink.h \ + linux/seccomp.h \ linux/userfaultfd.h \ mm.h \ netinet/sctp.h \
include/lapi/prctl.h +5 -0
@@ -9,6 +9,11 @@#include <sys/prctl.h> +#ifndef PR_SET_SECCOMP +# define PR_GET_SECCOMP 21 +# define PR_SET_SECCOMP 22 +#endif +#ifndef PR_SET_CHILD_SUBREAPER # define PR_SET_CHILD_SUBREAPER 36# define PR_GET_CHILD_SUBREAPER 37
include/lapi/seccomp.h new file +40 -0
@@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* +* Copyright (c) 2019 FUJITSU LIMITED. All rights reserved. +* Author: Yang Xu <[email protected]> +*/ +#ifndef LAPI_SECCOMP_H +#define LAPI_SECCOMP_H + +# include <linux/types.h> + +# ifdef HAVE_LINUX_SECCOMP_H +# include <linux/seccomp.h> +# else +/* Valid values for seccomp.mode and prctl(PR_SET_SECCOMP, <mode>) */ +# define SECCOMP_MODE_DISABLED 0 +# define SECCOMP_MODE_STRICT 1 +# define SECCOMP_MODE_FILTER 2 + +# define SECCOMP_RET_KILL_THREAD 0x00000000U/* kill the thread */ +# define SECCOMP_RET_KILL SECCOMP_RET_KILL_THREAD +# define SECCOMP_RET_ALLOW 0x7fff0000U/* allow */ + +/** +* struct seccomp_data - the format the BPF program executes over. +* @nr: the system call number +* @arch: indicates system call convention as an AUDIT_ARCH_* value +* as defined in <linux/audit.h>. +* @instruction_pointer: at the time of the system call. +* @args: up to 6 system call arguments always stored as 64-bit values +* regardless of the architecture. +*/ +struct seccomp_data { +int nr; +__u32 arch; +__u64 instruction_pointer; +__u64 args[6 ]; +}; + +# endif /* HAVE_LINUX_SECCOMP_H*/ +#endif /* LAPI_SECCOMP_H */
runtest/syscalls +1 -0
@@ -861,6 +861,7 @@ ppoll01 ppoll01 prctl01 prctl01 prctl02 prctl02 prctl03 prctl03 +prctl04 prctl04 pread01 pread01 pread01_64 pread01_64
testcases/kernel/syscalls/prctl/.gitignore +1 -0
@@ -1,3 +1,4 @@ /prctl01 /prctl02 /prctl03 +/prctl04
testcases/kernel/syscalls/prctl/prctl04.c new file +231 -0
231 lines not on this page · raw message
Signature · 1 line
2.18.1
PATCH v3 1/1 syscalls/prctl04.c: New test for prctl() with PR_{SET, GET}_SECCOMP
Hi!
Pushed, thanks.
Signature · 2 lines
Cyril Hrubis [email protected]