diff mbox series

[v3] syscalls/prctl02: add more error tests

Message ID 1572613170-20757-1-git-send-email-xuyang2018.jy@cn.fujitsu.com
State Changes Requested
Headers show
Series [v3] syscalls/prctl02: add more error tests | expand

Commit Message

Yang Xu Nov. 1, 2019, 12:59 p.m. UTC
------
v2->v3:
1.add missing <linux/capability.h>
------

Signed-off-by: Yang Xu <xuyang2018.jy@cn.fujitsu.com>
---
 include/lapi/prctl.h                      |  10 +++
 m4/ltp-prctl.m4                           |   3 +-
 testcases/kernel/syscalls/prctl/prctl02.c | 105 ++++++++++++++++++++--
 3 files changed, 111 insertions(+), 7 deletions(-)

Comments

Cyril Hrubis Nov. 7, 2019, 2:54 p.m. UTC | #1
Hi!
>  #include <errno.h>
>  #include <signal.h>
>  #include <sys/prctl.h>
> -
> +#include <linux/filter.h>
> +#include <linux/capability.h>
> +#include <unistd.h>
> +#include <stdlib.h>
> +#include <stddef.h>
> +#include "config.h"
> +#include "lapi/prctl.h"
> +#include "lapi/seccomp.h"
> +#include "lapi/syscalls.h"
>  #include "tst_test.h"
> +#include "tst_capability.h"
>  
>  #define OPTION_INVALID 999
>  #define INVALID_ARG 999
>  
> +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
> +};

We do have the exact same bytecode in the prctl04.c, can we put it to a
header and include it in both tests?

Or alternatively do we need more than just one-liner with
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW) here?

>  static struct tcase {
>  	int option;
>  	unsigned long arg2;
> +	unsigned long arg3;
>  	int exp_errno;
> +	int bad_addr;
>  } tcases[] = {
> -	{OPTION_INVALID, 0, EINVAL},
> -	{PR_SET_PDEATHSIG, INVALID_ARG, EINVAL},
> +	{OPTION_INVALID, 0, 0, EINVAL, 0},
> +	{PR_SET_PDEATHSIG, INVALID_ARG, 0, EINVAL, 0},
> +	{PR_SET_DUMPABLE, 2, 0, EINVAL, 0},
> +	{PR_SET_NAME, 0, 0, EFAULT, 1},
> +	{PR_SET_SECCOMP, 2, 0, EFAULT, 1},
> +	{PR_SET_SECCOMP, 2, 2, EACCES, 0},
> +	{PR_SET_TIMING, 1, 0, EINVAL, 0},
> +#ifdef HAVE_DECL_PR_SET_NO_NEW_PRIVS
> +	{PR_SET_NO_NEW_PRIVS, 0, 0, EINVAL, 0},
> +	{PR_SET_NO_NEW_PRIVS, 1, 1, EINVAL, 0},
> +	{PR_GET_NO_NEW_PRIVS, 1, 0, EINVAL, 0},
> +#endif
> +#ifdef HAVE_DECL_PR_SET_THP_DISABLE
> +	{PR_SET_THP_DISABLE, 0, 1, EINVAL, 0},
> +	{PR_GET_THP_DISABLE, 1, 0, EINVAL, 0},
> +#endif
> +#ifdef HAVE_DECL_PR_CAP_AMBIENT
> +	{PR_CAP_AMBIENT, 2, 1, EINVAL, 0},
> +#endif
> +#ifdef HAVE_DECL_PR_GET_SPECULATION_CTR
> +	{PR_GET_SPECULATION_CTRL, 1, 0, EINVAL, 0},
> +#endif
> +	{PR_SET_SECUREBITS, 0, 0, EPERM, 0},
> +	{PR_CAPBSET_DROP, 1, 0, EPERM, 0},
>  };
>  
>  static void verify_prctl(unsigned int n)
>  {
>  	struct tcase *tc = &tcases[n];
>  
> -	TEST(prctl(tc->option, tc->arg2));
> +	if (tc->arg3 == 2)
> +		tc->arg3 = (unsigned long)&strict;
> +	if (tc->bad_addr) {
> +		if (tc->arg2)
> +			tc->arg3 = (unsigned long)tst_get_bad_addr(NULL);
> +		else
> +			tc->arg2 = (unsigned long)tst_get_bad_addr(NULL);
> +	}

I do not like this hackery, can't we just change the test to use
pointers to pointers and initialize global variables in the test setup
as we usually do?

> +	TEST(prctl(tc->option, tc->arg2, tc->arg3));
>  	if (TST_RET == 0) {
>  		tst_res(TFAIL, "prctl() succeeded unexpectedly");
>  		return;
> @@ -38,7 +123,10 @@ static void verify_prctl(unsigned int n)
>  	if (tc->exp_errno == TST_ERR) {
>  		tst_res(TPASS | TTERRNO, "prctl() failed as expected");
>  	} else {
> -		tst_res(TFAIL | TTERRNO, "prctl() failed unexpectedly, expected %s",
> +		if (tc->option == PR_SET_SECCOMP && TST_ERR == EINVAL)
> +			tst_res(TCONF, "current system was not built with CONFIG_SECCOMP.");
> +		else
> +			tst_res(TFAIL | TTERRNO, "prctl() failed unexpectedly, expected %s",
>  				tst_strerrno(tc->exp_errno));
>  	}
>  }
> @@ -46,4 +134,9 @@ static void verify_prctl(unsigned int n)
>  static struct tst_test test = {
>  	.tcnt = ARRAY_SIZE(tcases),
>  	.test = verify_prctl,
> +	.caps = (struct tst_cap []) {
> +		TST_CAP(TST_CAP_DROP, CAP_SYS_ADMIN),
> +		TST_CAP(TST_CAP_DROP, CAP_SETPCAP),
> +		{}
> +	},
>  };
> -- 
> 2.18.0
> 
> 
> 
> 
> -- 
> Mailing list info: https://lists.linux.it/listinfo/ltp
Yang Xu Nov. 8, 2019, 12:12 p.m. UTC | #2
on 2019/11/07 22:54, Cyril Hrubis wrote:

> Hi!
>>   #include <errno.h>
>>   #include <signal.h>
>>   #include <sys/prctl.h>
>> -
>> +#include <linux/filter.h>
>> +#include <linux/capability.h>
>> +#include <unistd.h>
>> +#include <stdlib.h>
>> +#include <stddef.h>
>> +#include "config.h"
>> +#include "lapi/prctl.h"
>> +#include "lapi/seccomp.h"
>> +#include "lapi/syscalls.h"
>>   #include "tst_test.h"
>> +#include "tst_capability.h"
>>   
>>   #define OPTION_INVALID 999
>>   #define INVALID_ARG 999
>>   
>> +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
>> +};
> We do have the exact same bytecode in the prctl04.c, can we put it to a
> header and include it in both tests?
>
> Or alternatively do we need more than just one-liner with
> BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW) here?

we only need one-liner with BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW) here.

>
>>   static struct tcase {
>>   	int option;
>>   	unsigned long arg2;
>> +	unsigned long arg3;
>>   	int exp_errno;
>> +	int bad_addr;
>>   } tcases[] = {
>> -	{OPTION_INVALID, 0, EINVAL},
>> -	{PR_SET_PDEATHSIG, INVALID_ARG, EINVAL},
>> +	{OPTION_INVALID, 0, 0, EINVAL, 0},
>> +	{PR_SET_PDEATHSIG, INVALID_ARG, 0, EINVAL, 0},
>> +	{PR_SET_DUMPABLE, 2, 0, EINVAL, 0},
>> +	{PR_SET_NAME, 0, 0, EFAULT, 1},
>> +	{PR_SET_SECCOMP, 2, 0, EFAULT, 1},
>> +	{PR_SET_SECCOMP, 2, 2, EACCES, 0},
>> +	{PR_SET_TIMING, 1, 0, EINVAL, 0},
>> +#ifdef HAVE_DECL_PR_SET_NO_NEW_PRIVS
>> +	{PR_SET_NO_NEW_PRIVS, 0, 0, EINVAL, 0},
>> +	{PR_SET_NO_NEW_PRIVS, 1, 1, EINVAL, 0},
>> +	{PR_GET_NO_NEW_PRIVS, 1, 0, EINVAL, 0},
>> +#endif
>> +#ifdef HAVE_DECL_PR_SET_THP_DISABLE
>> +	{PR_SET_THP_DISABLE, 0, 1, EINVAL, 0},
>> +	{PR_GET_THP_DISABLE, 1, 0, EINVAL, 0},
>> +#endif
>> +#ifdef HAVE_DECL_PR_CAP_AMBIENT
>> +	{PR_CAP_AMBIENT, 2, 1, EINVAL, 0},
>> +#endif
>> +#ifdef HAVE_DECL_PR_GET_SPECULATION_CTR
>> +	{PR_GET_SPECULATION_CTRL, 1, 0, EINVAL, 0},
>> +#endif
>> +	{PR_SET_SECUREBITS, 0, 0, EPERM, 0},
>> +	{PR_CAPBSET_DROP, 1, 0, EPERM, 0},
>>   };
>>   
>>   static void verify_prctl(unsigned int n)
>>   {
>>   	struct tcase *tc = &tcases[n];
>>   
>> -	TEST(prctl(tc->option, tc->arg2));
>> +	if (tc->arg3 == 2)
>> +		tc->arg3 = (unsigned long)&strict;
>> +	if (tc->bad_addr) {
>> +		if (tc->arg2)
>> +			tc->arg3 = (unsigned long)tst_get_bad_addr(NULL);
>> +		else
>> +			tc->arg2 = (unsigned long)tst_get_bad_addr(NULL);
>> +	}
> I do not like this hackery, can't we just change the test to use
> pointers to pointers and initialize global variables in the test setup
> as we usually do?

Ok. I will do it as we usually do.

>
>> +	TEST(prctl(tc->option, tc->arg2, tc->arg3));
>>   	if (TST_RET == 0) {
>>   		tst_res(TFAIL, "prctl() succeeded unexpectedly");
>>   		return;
>> @@ -38,7 +123,10 @@ static void verify_prctl(unsigned int n)
>>   	if (tc->exp_errno == TST_ERR) {
>>   		tst_res(TPASS | TTERRNO, "prctl() failed as expected");
>>   	} else {
>> -		tst_res(TFAIL | TTERRNO, "prctl() failed unexpectedly, expected %s",
>> +		if (tc->option == PR_SET_SECCOMP && TST_ERR == EINVAL)
>> +			tst_res(TCONF, "current system was not built with CONFIG_SECCOMP.");
>> +		else
>> +			tst_res(TFAIL | TTERRNO, "prctl() failed unexpectedly, expected %s",
>>   				tst_strerrno(tc->exp_errno));
>>   	}
>>   }
>> @@ -46,4 +134,9 @@ static void verify_prctl(unsigned int n)
>>   static struct tst_test test = {
>>   	.tcnt = ARRAY_SIZE(tcases),
>>   	.test = verify_prctl,
>> +	.caps = (struct tst_cap []) {
>> +		TST_CAP(TST_CAP_DROP, CAP_SYS_ADMIN),
>> +		TST_CAP(TST_CAP_DROP, CAP_SETPCAP),
>> +		{}
>> +	},
>>   };
>> -- 
>> 2.18.0
>>
>>
>>
>>
>> -- 
>> Mailing list info: https://lists.linux.it/listinfo/ltp
Yang Xu Nov. 8, 2019, 1:20 p.m. UTC | #3
on 2019/11/08 20:12, Yang Xu wrote:

>
> on 2019/11/07 22:54, Cyril Hrubis wrote:
>> Hi!
>>>   #include <errno.h>
>>>   #include <signal.h>
>>>   #include <sys/prctl.h>
>>> -
>>> +#include <linux/filter.h>
>>> +#include <linux/capability.h>
>>> +#include <unistd.h>
>>> +#include <stdlib.h>
>>> +#include <stddef.h>
>>> +#include "config.h"
>>> +#include "lapi/prctl.h"
>>> +#include "lapi/seccomp.h"
>>> +#include "lapi/syscalls.h"
>>>   #include "tst_test.h"
>>> +#include "tst_capability.h"
>>>   
>>>   #define OPTION_INVALID 999
>>>   #define INVALID_ARG 999
>>>   
>>> +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
>>> +};
>> We do have the exact same bytecode in the prctl04.c, can we put it to a
>> header and include it in both tests?
>>
>> Or alternatively do we need more than just one-liner with
>> BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW) here?
> we only need one-liner with BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW) here.
>>>   static struct tcase {
>>>   	int option;
>>>   	unsigned long arg2;
>>> +	unsigned long arg3;
>>>   	int exp_errno;
>>> +	int bad_addr;
>>>   } tcases[] = {
>>> -	{OPTION_INVALID, 0, EINVAL},
>>> -	{PR_SET_PDEATHSIG, INVALID_ARG, EINVAL},
>>> +	{OPTION_INVALID, 0, 0, EINVAL, 0},
>>> +	{PR_SET_PDEATHSIG, INVALID_ARG, 0, EINVAL, 0},
>>> +	{PR_SET_DUMPABLE, 2, 0, EINVAL, 0},
>>> +	{PR_SET_NAME, 0, 0, EFAULT, 1},
>>> +	{PR_SET_SECCOMP, 2, 0, EFAULT, 1},
>>> +	{PR_SET_SECCOMP, 2, 2, EACCES, 0},
>>> +	{PR_SET_TIMING, 1, 0, EINVAL, 0},
>>> +#ifdef HAVE_DECL_PR_SET_NO_NEW_PRIVS
>>> +	{PR_SET_NO_NEW_PRIVS, 0, 0, EINVAL, 0},
>>> +	{PR_SET_NO_NEW_PRIVS, 1, 1, EINVAL, 0},
>>> +	{PR_GET_NO_NEW_PRIVS, 1, 0, EINVAL, 0},
>>> +#endif
>>> +#ifdef HAVE_DECL_PR_SET_THP_DISABLE
>>> +	{PR_SET_THP_DISABLE, 0, 1, EINVAL, 0},
>>> +	{PR_GET_THP_DISABLE, 1, 0, EINVAL, 0},
>>> +#endif
>>> +#ifdef HAVE_DECL_PR_CAP_AMBIENT
>>> +	{PR_CAP_AMBIENT, 2, 1, EINVAL, 0},
>>> +#endif
>>> +#ifdef HAVE_DECL_PR_GET_SPECULATION_CTR
>>> +	{PR_GET_SPECULATION_CTRL, 1, 0, EINVAL, 0},
>>> +#endif
>>> +	{PR_SET_SECUREBITS, 0, 0, EPERM, 0},
>>> +	{PR_CAPBSET_DROP, 1, 0, EPERM, 0},
>>>   };
>>>   
>>>   static void verify_prctl(unsigned int n)
>>>   {
>>>   	struct tcase *tc = &tcases[n];
>>>   
>>> -	TEST(prctl(tc->option, tc->arg2));
>>> +	if (tc->arg3 == 2)
>>> +		tc->arg3 = (unsigned long)&strict;
>>> +	if (tc->bad_addr) {
>>> +		if (tc->arg2)
>>> +			tc->arg3 = (unsigned long)tst_get_bad_addr(NULL);
>>> +		else
>>> +			tc->arg2 = (unsigned long)tst_get_bad_addr(NULL);
>>> +	}
>> I do not like this hackery, can't we just change the test to use
>> pointers to pointers and initialize global variables in the test setup
>> as we usually do?
> Ok. I will do it as we usually do.

  I think about it again. The argument of prctl is all unsigned long type. Do we need to use
pointers to pointers? Or, move this code to setup function like above?

>>> +	TEST(prctl(tc->option, tc->arg2, tc->arg3));
>>>   	if (TST_RET == 0) {
>>>   		tst_res(TFAIL, "prctl() succeeded unexpectedly");
>>>   		return;
>>> @@ -38,7 +123,10 @@ static void verify_prctl(unsigned int n)
>>>   	if (tc->exp_errno == TST_ERR) {
>>>   		tst_res(TPASS | TTERRNO, "prctl() failed as expected");
>>>   	} else {
>>> -		tst_res(TFAIL | TTERRNO, "prctl() failed unexpectedly, expected %s",
>>> +		if (tc->option == PR_SET_SECCOMP && TST_ERR == EINVAL)
>>> +			tst_res(TCONF, "current system was not built with CONFIG_SECCOMP.");
>>> +		else
>>> +			tst_res(TFAIL | TTERRNO, "prctl() failed unexpectedly, expected %s",
>>>   				tst_strerrno(tc->exp_errno));
>>>   	}
>>>   }
>>> @@ -46,4 +134,9 @@ static void verify_prctl(unsigned int n)
>>>   static struct tst_test test = {
>>>   	.tcnt = ARRAY_SIZE(tcases),
>>>   	.test = verify_prctl,
>>> +	.caps = (struct tst_cap []) {
>>> +		TST_CAP(TST_CAP_DROP, CAP_SYS_ADMIN),
>>> +		TST_CAP(TST_CAP_DROP, CAP_SETPCAP),
>>> +		{}
>>> +	},
>>>   };
>>> -- 
>>> 2.18.0
>>>
>>>
>>>
>>>
>>> -- 
>>> Mailing list info:https://lists.linux.it/listinfo/ltp
>
Cyril Hrubis Nov. 8, 2019, 2:24 p.m. UTC | #4
Hi!
> > Ok. I will do it as we usually do.
> 
>  ??I think about it again. The argument of prctl is all unsigned long type. Do we need to use
> pointers to pointers??? Or, move this code to setup function like above?

Just pointer to unsigned long and do the cast in the test setup as:

static unsigned long bad_addr;

static struct tcase = {
	...
	unsigned long *arg3;
	...
} tcases[] = {
	...
	{..., &bad_addr, ...},
	...
}

static void setup(void)
{
	invalid_addr = (unigned long)tst_get_bad_addr();
}

Then call it as prctl(..., *tc->arg3).
diff mbox series

Patch

diff --git a/include/lapi/prctl.h b/include/lapi/prctl.h
index 8ee492259..ea52ecac3 100644
--- a/include/lapi/prctl.h
+++ b/include/lapi/prctl.h
@@ -29,6 +29,11 @@ 
 # define PR_GET_NO_NEW_PRIVS 39
 #endif
 
+#ifndef PR_SET_THP_DISABLE
+# define PR_SET_THP_DISABLE 41
+# define PR_GET_THP_DISABLE 42
+#endif
+
 #ifndef PR_CAP_AMBIENT
 # define PR_CAP_AMBIENT             47
 # define PR_CAP_AMBIENT_IS_SET      1
@@ -37,4 +42,9 @@ 
 # define PR_CAP_AMBIENT_CLEAR_ALL   4
 #endif
 
+#ifndef PR_GET_SPECULATION_CTRL
+# define PR_GET_SPECULATION_CTRL 52
+# define PR_SET_SPECULATION_CTRL 53
+#endif
+
 #endif /* LAPI_PRCTL_H__ */
diff --git a/m4/ltp-prctl.m4 b/m4/ltp-prctl.m4
index e429db8fe..b592789ee 100644
--- a/m4/ltp-prctl.m4
+++ b/m4/ltp-prctl.m4
@@ -4,7 +4,8 @@  dnl Author: Ngie Cooper <yaneurabeya@gmail.com>
 
 AC_DEFUN([LTP_CHECK_PRCTL_SUPPORT],[
 AC_CHECK_HEADERS(sys/prctl.h,[
-	AC_CHECK_DECLS([PR_CAPBSET_DROP, PR_CAPBSET_READ], [],[],[
+	AC_CHECK_DECLS([PR_CAPBSET_DROP, PR_CAPBSET_READ, PR_CAP_AMBIENT,
+PR_SET_NO_NEW_PRIVS, PR_GET_SPECULATION_CTRL, PR_SET_THP_DISABLE], [],[],[
 #include <sys/prctl.h>
 ]) dnl AC_CHECK_DECLS
 ])]
diff --git a/testcases/kernel/syscalls/prctl/prctl02.c b/testcases/kernel/syscalls/prctl/prctl02.c
index ec45911fd..2e0bbf1dc 100644
--- a/testcases/kernel/syscalls/prctl/prctl02.c
+++ b/testcases/kernel/syscalls/prctl/prctl02.c
@@ -4,32 +4,117 @@ 
  *
  * 1) prctl() fails with EINVAL when an invalid value is given for option
  * 2) prctl() fails with EINVAL when option is PR_SET_PDEATHSIG & arg2 is
- * not zero or a valid signal number
+ * not zero or a valid signal number.
+ * 3) prctl() fails with EINVAL when option is PR_SET_DUMPABLE & arg2 is
+ * neither SUID_DUMP_DISABLE nor SUID_DUMP_USER.
+ * 4) prctl() fails with EFAULT when arg2 is an invalid address.
+ * 5) prctl() fails with EFAULT when option is PR_SET_SECCOMP & arg2 is
+ * SECCOMP_MODE_FILTER & arg3 is an invalid address.
+ * 6) prctl() fails with EACCES when option is PR_SET_SECCOMP & arg2 is
+ * SECCOMP_MODE_FILTER & the process does not have the CAP_SYS_ADMIN
+ * capability.
+ * 7) prctl() fails with EINVAL when option is PR_SET_TIMING & arg2 is not
+ * not PR_TIMING_STATISTICAL.
+ * 8,9) prctl() fails with EINVAL when option is PR_SET_NO_NEW_PRIVS & arg2
+ * is not equal to 1 or arg3 is nonzero.
+ * 10) prctl() fails with EINVAL when options is PR_GET_NO_NEW_PRIVS & arg2,
+ * arg3, arg4, or arg5 is nonzero.
+ * 11) prctl() fails with EINVAL when options is PR_SET_THP_DISABLE & arg3,
+ * arg4, arg5 is non-zero.
+ * 12) prctl() fails with EINVAL when options is PR_GET_THP_DISABLE & arg2,
+ * arg3, arg4, or arg5 is nonzero.
+ * 13) prctl() fails with EINVAL when options is PR_CAP_AMBIENT & an unused
+ * argument such as arg4 is nonzero.
+ * 14) prctl() fails with EINVAL when option is PR_GET_SPECULATION_CTRL and
+ * unused arguments is nonzero.
+ * 15) prctl() fails with EPERM when option is PR_SET_SECUREBITS and the
+ * caller does not have the CAP_SETPCAP capability.
+ * 16) prctl() fails with EPERM when option is PR_CAPBSET_DROP and the caller
+ * does not have the CAP_SETPCAP capability.
  */
 
 #include <errno.h>
 #include <signal.h>
 #include <sys/prctl.h>
-
+#include <linux/filter.h>
+#include <linux/capability.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include "config.h"
+#include "lapi/prctl.h"
+#include "lapi/seccomp.h"
+#include "lapi/syscalls.h"
 #include "tst_test.h"
+#include "tst_capability.h"
 
 #define OPTION_INVALID 999
 #define INVALID_ARG 999
 
+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 struct tcase {
 	int option;
 	unsigned long arg2;
+	unsigned long arg3;
 	int exp_errno;
+	int bad_addr;
 } tcases[] = {
-	{OPTION_INVALID, 0, EINVAL},
-	{PR_SET_PDEATHSIG, INVALID_ARG, EINVAL},
+	{OPTION_INVALID, 0, 0, EINVAL, 0},
+	{PR_SET_PDEATHSIG, INVALID_ARG, 0, EINVAL, 0},
+	{PR_SET_DUMPABLE, 2, 0, EINVAL, 0},
+	{PR_SET_NAME, 0, 0, EFAULT, 1},
+	{PR_SET_SECCOMP, 2, 0, EFAULT, 1},
+	{PR_SET_SECCOMP, 2, 2, EACCES, 0},
+	{PR_SET_TIMING, 1, 0, EINVAL, 0},
+#ifdef HAVE_DECL_PR_SET_NO_NEW_PRIVS
+	{PR_SET_NO_NEW_PRIVS, 0, 0, EINVAL, 0},
+	{PR_SET_NO_NEW_PRIVS, 1, 1, EINVAL, 0},
+	{PR_GET_NO_NEW_PRIVS, 1, 0, EINVAL, 0},
+#endif
+#ifdef HAVE_DECL_PR_SET_THP_DISABLE
+	{PR_SET_THP_DISABLE, 0, 1, EINVAL, 0},
+	{PR_GET_THP_DISABLE, 1, 0, EINVAL, 0},
+#endif
+#ifdef HAVE_DECL_PR_CAP_AMBIENT
+	{PR_CAP_AMBIENT, 2, 1, EINVAL, 0},
+#endif
+#ifdef HAVE_DECL_PR_GET_SPECULATION_CTR
+	{PR_GET_SPECULATION_CTRL, 1, 0, EINVAL, 0},
+#endif
+	{PR_SET_SECUREBITS, 0, 0, EPERM, 0},
+	{PR_CAPBSET_DROP, 1, 0, EPERM, 0},
 };
 
 static void verify_prctl(unsigned int n)
 {
 	struct tcase *tc = &tcases[n];
 
-	TEST(prctl(tc->option, tc->arg2));
+	if (tc->arg3 == 2)
+		tc->arg3 = (unsigned long)&strict;
+	if (tc->bad_addr) {
+		if (tc->arg2)
+			tc->arg3 = (unsigned long)tst_get_bad_addr(NULL);
+		else
+			tc->arg2 = (unsigned long)tst_get_bad_addr(NULL);
+	}
+	TEST(prctl(tc->option, tc->arg2, tc->arg3));
 	if (TST_RET == 0) {
 		tst_res(TFAIL, "prctl() succeeded unexpectedly");
 		return;
@@ -38,7 +123,10 @@  static void verify_prctl(unsigned int n)
 	if (tc->exp_errno == TST_ERR) {
 		tst_res(TPASS | TTERRNO, "prctl() failed as expected");
 	} else {
-		tst_res(TFAIL | TTERRNO, "prctl() failed unexpectedly, expected %s",
+		if (tc->option == PR_SET_SECCOMP && TST_ERR == EINVAL)
+			tst_res(TCONF, "current system was not built with CONFIG_SECCOMP.");
+		else
+			tst_res(TFAIL | TTERRNO, "prctl() failed unexpectedly, expected %s",
 				tst_strerrno(tc->exp_errno));
 	}
 }
@@ -46,4 +134,9 @@  static void verify_prctl(unsigned int n)
 static struct tst_test test = {
 	.tcnt = ARRAY_SIZE(tcases),
 	.test = verify_prctl,
+	.caps = (struct tst_cap []) {
+		TST_CAP(TST_CAP_DROP, CAP_SYS_ADMIN),
+		TST_CAP(TST_CAP_DROP, CAP_SETPCAP),
+		{}
+	},
 };