diff mbox series

[v3,2/5] idmapped-mounts: Add mknodat operation in setgid test

Message ID 1649763226-2329-2-git-send-email-xuyang2018.jy@fujitsu.com (mailing list archive)
State New, archived
Headers show
Series [v3,1/5] idmapped-mounts: Reset errno to zero after detect fs_allow_idmap | expand

Commit Message

Yang Xu (Fujitsu) April 12, 2022, 11:33 a.m. UTC
Since mknodat can create file, we should also check whether strip S_ISGID.
Also add new helper caps_down_fsetid to drop CAP_FSETID because strip S_ISGID
depend on this cap and keep other cap(ie CAP_MKNOD) because create character
device needs it when using mknod.

Only test mknodat with character device in setgid_create function and the another
two functions test mknodat with whiteout device.

Since kernel commit a3c751a50 ("vfs: allow unprivileged whiteout creation") in
v5.8-rc1, we can create whiteout device in userns test. Since kernel 5.12, mount_setattr
and MOUNT_ATTR_IDMAP was supported, we don't need to detect kernel whether allow
unprivileged whiteout creation. Using fs_allow_idmap as a proxy is safe.

Tested-by: Christian Brauner (Microsoft)<brauner@kernel.org>
Reviewed-by: Christian Brauner (Microsoft)<brauner@kernel.org>
Signed-off-by: Yang Xu <xuyang2018.jy@fujitsu.com>
---
 src/idmapped-mounts/idmapped-mounts.c | 219 +++++++++++++++++++++++++-
 1 file changed, 213 insertions(+), 6 deletions(-)

Comments

Christian Brauner April 13, 2022, 7:59 a.m. UTC | #1
On Tue, Apr 12, 2022 at 07:33:43PM +0800, Yang Xu wrote:
> Since mknodat can create file, we should also check whether strip S_ISGID.
> Also add new helper caps_down_fsetid to drop CAP_FSETID because strip S_ISGID
> depend on this cap and keep other cap(ie CAP_MKNOD) because create character
> device needs it when using mknod.
> 
> Only test mknodat with character device in setgid_create function and the another
> two functions test mknodat with whiteout device.
> 
> Since kernel commit a3c751a50 ("vfs: allow unprivileged whiteout creation") in
> v5.8-rc1, we can create whiteout device in userns test. Since kernel 5.12, mount_setattr
> and MOUNT_ATTR_IDMAP was supported, we don't need to detect kernel whether allow
> unprivileged whiteout creation. Using fs_allow_idmap as a proxy is safe.
> 
> Tested-by: Christian Brauner (Microsoft)<brauner@kernel.org>
> Reviewed-by: Christian Brauner (Microsoft)<brauner@kernel.org>
> Signed-off-by: Yang Xu <xuyang2018.jy@fujitsu.com>
> ---
>  src/idmapped-mounts/idmapped-mounts.c | 219 +++++++++++++++++++++++++-
>  1 file changed, 213 insertions(+), 6 deletions(-)
> 
> diff --git a/src/idmapped-mounts/idmapped-mounts.c b/src/idmapped-mounts/idmapped-mounts.c
> index 8e6405c5..617f56e0 100644
> --- a/src/idmapped-mounts/idmapped-mounts.c
> +++ b/src/idmapped-mounts/idmapped-mounts.c
> @@ -241,6 +241,34 @@ static inline bool caps_supported(void)
>  	return ret;
>  }
>  
> +static int caps_down_fsetid(void)
> +{
> +	bool fret = false;
> +#ifdef HAVE_SYS_CAPABILITY_H
> +	cap_t caps = NULL;
> +	cap_value_t cap = CAP_FSETID;
> +	int ret = -1;
> +
> +	caps = cap_get_proc();
> +	if (!caps)
> +		goto out;
> +
> +	ret = cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, 0);
> +	if (ret)
> +		goto out;
> +
> +	ret = cap_set_proc(caps);
> +	if (ret)
> +		goto out;
> +
> +	fret = true;
> +
> +out:
> +	cap_free(caps);
> +#endif
> +	return fret;
> +}
> +
>  /* caps_down - lower all effective caps */
>  static int caps_down(void)
>  {
> @@ -7805,8 +7833,8 @@ out_unmap:
>  #endif /* HAVE_LIBURING_H */
>  
>  /* The following tests are concerned with setgid inheritance. These can be
> - * filesystem type specific. For xfs, if a new file or directory is created
> - * within a setgid directory and irix_sgid_inhiert is set then inherit the
> + * filesystem type specific. For xfs, if a new file or directory or node is
> + * created within a setgid directory and irix_sgid_inhiert is set then inherit the
>   * setgid bit if the caller is in the group of the directory.
>   */
>  static int setgid_create(void)
> @@ -7863,18 +7891,44 @@ static int setgid_create(void)
>  		if (!is_setgid(t_dir1_fd, DIR1, 0))
>  			die("failure: is_setgid");
>  
> +		/* create a special file via mknodat() vfs_create */
> +		if (mknodat(t_dir1_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0))
> +			die("failure: mknodat");
> +
> +		if (!is_setgid(t_dir1_fd, FILE2, 0))
> +			die("failure: is_setgid");
> +
> +		/* create a character device via mknodat() vfs_mknod */
> +		if (mknodat(t_dir1_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, makedev(5, 1)))
> +			die("failure: mknodat");
> +
> +		if (!is_setgid(t_dir1_fd, CHRDEV1, 0))
> +			die("failure: is_setgid");
> +
>  		if (!expected_uid_gid(t_dir1_fd, FILE1, 0, 0, 0))
>  			die("failure: check ownership");
>  
>  		if (!expected_uid_gid(t_dir1_fd, DIR1, 0, 0, 0))
>  			die("failure: check ownership");
>  
> +		if (!expected_uid_gid(t_dir1_fd, FILE2, 0, 0, 0))
> +			die("failure: check ownership");
> +
> +		if (!expected_uid_gid(t_dir1_fd, CHRDEV1, 0, 0, 0))
> +			die("failure: check ownership");
> +
>  		if (unlinkat(t_dir1_fd, FILE1, 0))
>  			die("failure: delete");
>  
>  		if (unlinkat(t_dir1_fd, DIR1, AT_REMOVEDIR))
>  			die("failure: delete");
>  
> +		if (unlinkat(t_dir1_fd, FILE2, 0))
> +			die("failure: delete");
> +
> +		if (unlinkat(t_dir1_fd, CHRDEV1, 0))
> +			die("failure: delete");
> +
>  		exit(EXIT_SUCCESS);
>  	}
>  	if (wait_for_pid(pid))
> @@ -7889,8 +7943,8 @@ static int setgid_create(void)
>  		if (!switch_ids(0, 10000))
>  			die("failure: switch_ids");
>  
> -		if (!caps_down())
> -			die("failure: caps_down");
> +		if (!caps_down_fsetid())
> +			die("failure: caps_down_fsetid");
>  
>  		/* create regular file via open() */
>  		file1_fd = openat(t_dir1_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID);
> @@ -7917,6 +7971,19 @@ static int setgid_create(void)
>  				die("failure: is_setgid");
>  		}

Please see my comment on the earlier thread:
https://lore.kernel.org/linux-fsdevel/20220407134009.s4shhomfxjz5cf5r@wittgenstein

The same issue still exists afaict, i.e. you're not handling the irix
case.
Yang Xu (Fujitsu) April 13, 2022, 8:31 a.m. UTC | #2
on 2022/4/13 15:59, Christian Brauner wrote:
> On Tue, Apr 12, 2022 at 07:33:43PM +0800, Yang Xu wrote:
>> Since mknodat can create file, we should also check whether strip S_ISGID.
>> Also add new helper caps_down_fsetid to drop CAP_FSETID because strip S_ISGID
>> depend on this cap and keep other cap(ie CAP_MKNOD) because create character
>> device needs it when using mknod.
>>
>> Only test mknodat with character device in setgid_create function and the another
>> two functions test mknodat with whiteout device.
>>
>> Since kernel commit a3c751a50 ("vfs: allow unprivileged whiteout creation") in
>> v5.8-rc1, we can create whiteout device in userns test. Since kernel 5.12, mount_setattr
>> and MOUNT_ATTR_IDMAP was supported, we don't need to detect kernel whether allow
>> unprivileged whiteout creation. Using fs_allow_idmap as a proxy is safe.
>>
>> Tested-by: Christian Brauner (Microsoft)<brauner@kernel.org>
>> Reviewed-by: Christian Brauner (Microsoft)<brauner@kernel.org>
>> Signed-off-by: Yang Xu<xuyang2018.jy@fujitsu.com>
>> ---
>>   src/idmapped-mounts/idmapped-mounts.c | 219 +++++++++++++++++++++++++-
>>   1 file changed, 213 insertions(+), 6 deletions(-)
>>
>> diff --git a/src/idmapped-mounts/idmapped-mounts.c b/src/idmapped-mounts/idmapped-mounts.c
>> index 8e6405c5..617f56e0 100644
>> --- a/src/idmapped-mounts/idmapped-mounts.c
>> +++ b/src/idmapped-mounts/idmapped-mounts.c
>> @@ -241,6 +241,34 @@ static inline bool caps_supported(void)
>>   	return ret;
>>   }
>>
>> +static int caps_down_fsetid(void)
>> +{
>> +	bool fret = false;
>> +#ifdef HAVE_SYS_CAPABILITY_H
>> +	cap_t caps = NULL;
>> +	cap_value_t cap = CAP_FSETID;
>> +	int ret = -1;
>> +
>> +	caps = cap_get_proc();
>> +	if (!caps)
>> +		goto out;
>> +
>> +	ret = cap_set_flag(caps, CAP_EFFECTIVE, 1,&cap, 0);
>> +	if (ret)
>> +		goto out;
>> +
>> +	ret = cap_set_proc(caps);
>> +	if (ret)
>> +		goto out;
>> +
>> +	fret = true;
>> +
>> +out:
>> +	cap_free(caps);
>> +#endif
>> +	return fret;
>> +}
>> +
>>   /* caps_down - lower all effective caps */
>>   static int caps_down(void)
>>   {
>> @@ -7805,8 +7833,8 @@ out_unmap:
>>   #endif /* HAVE_LIBURING_H */
>>
>>   /* The following tests are concerned with setgid inheritance. These can be
>> - * filesystem type specific. For xfs, if a new file or directory is created
>> - * within a setgid directory and irix_sgid_inhiert is set then inherit the
>> + * filesystem type specific. For xfs, if a new file or directory or node is
>> + * created within a setgid directory and irix_sgid_inhiert is set then inherit the
>>    * setgid bit if the caller is in the group of the directory.
>>    */
>>   static int setgid_create(void)
>> @@ -7863,18 +7891,44 @@ static int setgid_create(void)
>>   		if (!is_setgid(t_dir1_fd, DIR1, 0))
>>   			die("failure: is_setgid");
>>
>> +		/* create a special file via mknodat() vfs_create */
>> +		if (mknodat(t_dir1_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0))
>> +			die("failure: mknodat");
>> +
>> +		if (!is_setgid(t_dir1_fd, FILE2, 0))
>> +			die("failure: is_setgid");
>> +
>> +		/* create a character device via mknodat() vfs_mknod */
>> +		if (mknodat(t_dir1_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, makedev(5, 1)))
>> +			die("failure: mknodat");
>> +
>> +		if (!is_setgid(t_dir1_fd, CHRDEV1, 0))
>> +			die("failure: is_setgid");
>> +
>>   		if (!expected_uid_gid(t_dir1_fd, FILE1, 0, 0, 0))
>>   			die("failure: check ownership");
>>
>>   		if (!expected_uid_gid(t_dir1_fd, DIR1, 0, 0, 0))
>>   			die("failure: check ownership");
>>
>> +		if (!expected_uid_gid(t_dir1_fd, FILE2, 0, 0, 0))
>> +			die("failure: check ownership");
>> +
>> +		if (!expected_uid_gid(t_dir1_fd, CHRDEV1, 0, 0, 0))
>> +			die("failure: check ownership");
>> +
>>   		if (unlinkat(t_dir1_fd, FILE1, 0))
>>   			die("failure: delete");
>>
>>   		if (unlinkat(t_dir1_fd, DIR1, AT_REMOVEDIR))
>>   			die("failure: delete");
>>
>> +		if (unlinkat(t_dir1_fd, FILE2, 0))
>> +			die("failure: delete");
>> +
>> +		if (unlinkat(t_dir1_fd, CHRDEV1, 0))
>> +			die("failure: delete");
>> +
>>   		exit(EXIT_SUCCESS);
>>   	}
>>   	if (wait_for_pid(pid))
>> @@ -7889,8 +7943,8 @@ static int setgid_create(void)
>>   		if (!switch_ids(0, 10000))
>>   			die("failure: switch_ids");
>>
>> -		if (!caps_down())
>> -			die("failure: caps_down");
>> +		if (!caps_down_fsetid())
>> +			die("failure: caps_down_fsetid");
>>
>>   		/* create regular file via open() */
>>   		file1_fd = openat(t_dir1_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID);
>> @@ -7917,6 +7971,19 @@ static int setgid_create(void)
>>   				die("failure: is_setgid");
>>   		}
>
> Please see my comment on the earlier thread:
> https://lore.kernel.org/linux-fsdevel/20220407134009.s4shhomfxjz5cf5r@wittgenstein
>
> The same issue still exists afaict, i.e. you're not handling the irix
> case.
I remember it has two issues
1)replace 0755 with valid flags
2)consider fs.xfs.irix_sgid_inherit enable situation because it will 
strip S_ISGID for directory

I used t_dir1_fd directory instead of old DIR1, so I don't need to raise 
the S_ISGID when we enable fs.xfs.irix_sgid_inherit.

I have tested this on enable and disable fs.xfs.irix_sgid_inherit 
situations, they all pass.

Or I miss something?

Best Regards
Yang Xu
Christian Brauner April 13, 2022, 9:05 a.m. UTC | #3
On Wed, Apr 13, 2022 at 08:31:45AM +0000, xuyang2018.jy@fujitsu.com wrote:
> on 2022/4/13 15:59, Christian Brauner wrote:
> > On Tue, Apr 12, 2022 at 07:33:43PM +0800, Yang Xu wrote:
> >> Since mknodat can create file, we should also check whether strip S_ISGID.
> >> Also add new helper caps_down_fsetid to drop CAP_FSETID because strip S_ISGID
> >> depend on this cap and keep other cap(ie CAP_MKNOD) because create character
> >> device needs it when using mknod.
> >>
> >> Only test mknodat with character device in setgid_create function and the another
> >> two functions test mknodat with whiteout device.
> >>
> >> Since kernel commit a3c751a50 ("vfs: allow unprivileged whiteout creation") in
> >> v5.8-rc1, we can create whiteout device in userns test. Since kernel 5.12, mount_setattr
> >> and MOUNT_ATTR_IDMAP was supported, we don't need to detect kernel whether allow
> >> unprivileged whiteout creation. Using fs_allow_idmap as a proxy is safe.
> >>
> >> Tested-by: Christian Brauner (Microsoft)<brauner@kernel.org>
> >> Reviewed-by: Christian Brauner (Microsoft)<brauner@kernel.org>
> >> Signed-off-by: Yang Xu<xuyang2018.jy@fujitsu.com>
> >> ---
> >>   src/idmapped-mounts/idmapped-mounts.c | 219 +++++++++++++++++++++++++-
> >>   1 file changed, 213 insertions(+), 6 deletions(-)
> >>
> >> diff --git a/src/idmapped-mounts/idmapped-mounts.c b/src/idmapped-mounts/idmapped-mounts.c
> >> index 8e6405c5..617f56e0 100644
> >> --- a/src/idmapped-mounts/idmapped-mounts.c
> >> +++ b/src/idmapped-mounts/idmapped-mounts.c
> >> @@ -241,6 +241,34 @@ static inline bool caps_supported(void)
> >>   	return ret;
> >>   }
> >>
> >> +static int caps_down_fsetid(void)
> >> +{
> >> +	bool fret = false;
> >> +#ifdef HAVE_SYS_CAPABILITY_H
> >> +	cap_t caps = NULL;
> >> +	cap_value_t cap = CAP_FSETID;
> >> +	int ret = -1;
> >> +
> >> +	caps = cap_get_proc();
> >> +	if (!caps)
> >> +		goto out;
> >> +
> >> +	ret = cap_set_flag(caps, CAP_EFFECTIVE, 1,&cap, 0);
> >> +	if (ret)
> >> +		goto out;
> >> +
> >> +	ret = cap_set_proc(caps);
> >> +	if (ret)
> >> +		goto out;
> >> +
> >> +	fret = true;
> >> +
> >> +out:
> >> +	cap_free(caps);
> >> +#endif
> >> +	return fret;
> >> +}
> >> +
> >>   /* caps_down - lower all effective caps */
> >>   static int caps_down(void)
> >>   {
> >> @@ -7805,8 +7833,8 @@ out_unmap:
> >>   #endif /* HAVE_LIBURING_H */
> >>
> >>   /* The following tests are concerned with setgid inheritance. These can be
> >> - * filesystem type specific. For xfs, if a new file or directory is created
> >> - * within a setgid directory and irix_sgid_inhiert is set then inherit the
> >> + * filesystem type specific. For xfs, if a new file or directory or node is
> >> + * created within a setgid directory and irix_sgid_inhiert is set then inherit the
> >>    * setgid bit if the caller is in the group of the directory.
> >>    */
> >>   static int setgid_create(void)
> >> @@ -7863,18 +7891,44 @@ static int setgid_create(void)
> >>   		if (!is_setgid(t_dir1_fd, DIR1, 0))
> >>   			die("failure: is_setgid");
> >>
> >> +		/* create a special file via mknodat() vfs_create */
> >> +		if (mknodat(t_dir1_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0))
> >> +			die("failure: mknodat");
> >> +
> >> +		if (!is_setgid(t_dir1_fd, FILE2, 0))
> >> +			die("failure: is_setgid");
> >> +
> >> +		/* create a character device via mknodat() vfs_mknod */
> >> +		if (mknodat(t_dir1_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, makedev(5, 1)))
> >> +			die("failure: mknodat");
> >> +
> >> +		if (!is_setgid(t_dir1_fd, CHRDEV1, 0))
> >> +			die("failure: is_setgid");
> >> +
> >>   		if (!expected_uid_gid(t_dir1_fd, FILE1, 0, 0, 0))
> >>   			die("failure: check ownership");
> >>
> >>   		if (!expected_uid_gid(t_dir1_fd, DIR1, 0, 0, 0))
> >>   			die("failure: check ownership");
> >>
> >> +		if (!expected_uid_gid(t_dir1_fd, FILE2, 0, 0, 0))
> >> +			die("failure: check ownership");
> >> +
> >> +		if (!expected_uid_gid(t_dir1_fd, CHRDEV1, 0, 0, 0))
> >> +			die("failure: check ownership");
> >> +
> >>   		if (unlinkat(t_dir1_fd, FILE1, 0))
> >>   			die("failure: delete");
> >>
> >>   		if (unlinkat(t_dir1_fd, DIR1, AT_REMOVEDIR))
> >>   			die("failure: delete");
> >>
> >> +		if (unlinkat(t_dir1_fd, FILE2, 0))
> >> +			die("failure: delete");
> >> +
> >> +		if (unlinkat(t_dir1_fd, CHRDEV1, 0))
> >> +			die("failure: delete");
> >> +
> >>   		exit(EXIT_SUCCESS);
> >>   	}
> >>   	if (wait_for_pid(pid))
> >> @@ -7889,8 +7943,8 @@ static int setgid_create(void)
> >>   		if (!switch_ids(0, 10000))
> >>   			die("failure: switch_ids");
> >>
> >> -		if (!caps_down())
> >> -			die("failure: caps_down");
> >> +		if (!caps_down_fsetid())
> >> +			die("failure: caps_down_fsetid");
> >>
> >>   		/* create regular file via open() */
> >>   		file1_fd = openat(t_dir1_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID);
> >> @@ -7917,6 +7971,19 @@ static int setgid_create(void)
> >>   				die("failure: is_setgid");
> >>   		}
> >
> > Please see my comment on the earlier thread:
> > https://lore.kernel.org/linux-fsdevel/20220407134009.s4shhomfxjz5cf5r@wittgenstein
> >
> > The same issue still exists afaict, i.e. you're not handling the irix
> > case.
> I remember it has two issues
> 1)replace 0755 with valid flags
> 2)consider fs.xfs.irix_sgid_inherit enable situation because it will 
> strip S_ISGID for directory
> 
> I used t_dir1_fd directory instead of old DIR1, so I don't need to raise 

Ah, sorry, I missed this. Then this should all be fine:
Reviewed-by: Christian Brauner (Microsoft) <brauner@kernel.org>
diff mbox series

Patch

diff --git a/src/idmapped-mounts/idmapped-mounts.c b/src/idmapped-mounts/idmapped-mounts.c
index 8e6405c5..617f56e0 100644
--- a/src/idmapped-mounts/idmapped-mounts.c
+++ b/src/idmapped-mounts/idmapped-mounts.c
@@ -241,6 +241,34 @@  static inline bool caps_supported(void)
 	return ret;
 }
 
+static int caps_down_fsetid(void)
+{
+	bool fret = false;
+#ifdef HAVE_SYS_CAPABILITY_H
+	cap_t caps = NULL;
+	cap_value_t cap = CAP_FSETID;
+	int ret = -1;
+
+	caps = cap_get_proc();
+	if (!caps)
+		goto out;
+
+	ret = cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, 0);
+	if (ret)
+		goto out;
+
+	ret = cap_set_proc(caps);
+	if (ret)
+		goto out;
+
+	fret = true;
+
+out:
+	cap_free(caps);
+#endif
+	return fret;
+}
+
 /* caps_down - lower all effective caps */
 static int caps_down(void)
 {
@@ -7805,8 +7833,8 @@  out_unmap:
 #endif /* HAVE_LIBURING_H */
 
 /* The following tests are concerned with setgid inheritance. These can be
- * filesystem type specific. For xfs, if a new file or directory is created
- * within a setgid directory and irix_sgid_inhiert is set then inherit the
+ * filesystem type specific. For xfs, if a new file or directory or node is
+ * created within a setgid directory and irix_sgid_inhiert is set then inherit the
  * setgid bit if the caller is in the group of the directory.
  */
 static int setgid_create(void)
@@ -7863,18 +7891,44 @@  static int setgid_create(void)
 		if (!is_setgid(t_dir1_fd, DIR1, 0))
 			die("failure: is_setgid");
 
+		/* create a special file via mknodat() vfs_create */
+		if (mknodat(t_dir1_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0))
+			die("failure: mknodat");
+
+		if (!is_setgid(t_dir1_fd, FILE2, 0))
+			die("failure: is_setgid");
+
+		/* create a character device via mknodat() vfs_mknod */
+		if (mknodat(t_dir1_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, makedev(5, 1)))
+			die("failure: mknodat");
+
+		if (!is_setgid(t_dir1_fd, CHRDEV1, 0))
+			die("failure: is_setgid");
+
 		if (!expected_uid_gid(t_dir1_fd, FILE1, 0, 0, 0))
 			die("failure: check ownership");
 
 		if (!expected_uid_gid(t_dir1_fd, DIR1, 0, 0, 0))
 			die("failure: check ownership");
 
+		if (!expected_uid_gid(t_dir1_fd, FILE2, 0, 0, 0))
+			die("failure: check ownership");
+
+		if (!expected_uid_gid(t_dir1_fd, CHRDEV1, 0, 0, 0))
+			die("failure: check ownership");
+
 		if (unlinkat(t_dir1_fd, FILE1, 0))
 			die("failure: delete");
 
 		if (unlinkat(t_dir1_fd, DIR1, AT_REMOVEDIR))
 			die("failure: delete");
 
+		if (unlinkat(t_dir1_fd, FILE2, 0))
+			die("failure: delete");
+
+		if (unlinkat(t_dir1_fd, CHRDEV1, 0))
+			die("failure: delete");
+
 		exit(EXIT_SUCCESS);
 	}
 	if (wait_for_pid(pid))
@@ -7889,8 +7943,8 @@  static int setgid_create(void)
 		if (!switch_ids(0, 10000))
 			die("failure: switch_ids");
 
-		if (!caps_down())
-			die("failure: caps_down");
+		if (!caps_down_fsetid())
+			die("failure: caps_down_fsetid");
 
 		/* create regular file via open() */
 		file1_fd = openat(t_dir1_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID);
@@ -7917,6 +7971,19 @@  static int setgid_create(void)
 				die("failure: is_setgid");
 		}
 
+		/* create a special file via mknodat() vfs_create */
+		if (mknodat(t_dir1_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0))
+			die("failure: mknodat");
+
+		if (is_setgid(t_dir1_fd, FILE2, 0))
+			die("failure: is_setgid");
+
+		/* create a character device via mknodat() vfs_mknod */
+		if (mknodat(t_dir1_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, makedev(5, 1)))
+			die("failure: mknodat");
+
+		if (is_setgid(t_dir1_fd, CHRDEV1, 0))
+			die("failure: is_setgid");
 		/*
 		 * In setgid directories newly created files always inherit the
 		 * gid from the parent directory. Verify that the file is owned
@@ -7933,6 +8000,24 @@  static int setgid_create(void)
 		if (!expected_uid_gid(t_dir1_fd, DIR1, 0, 0, 0))
 			die("failure: check ownership");
 
+		if (!expected_uid_gid(t_dir1_fd, FILE2, 0, 0, 0))
+			die("failure: check ownership");
+
+		if (!expected_uid_gid(t_dir1_fd, CHRDEV1, 0, 0, 0))
+			die("failure: check ownership");
+
+		if (unlinkat(t_dir1_fd, FILE1, 0))
+			die("failure: delete");
+
+		if (unlinkat(t_dir1_fd, DIR1, AT_REMOVEDIR))
+			die("failure: delete");
+
+		if (unlinkat(t_dir1_fd, FILE2, 0))
+			die("failure: delete");
+
+		if (unlinkat(t_dir1_fd, CHRDEV1, 0))
+			die("failure: delete");
+
 		exit(EXIT_SUCCESS);
 	}
 	if (wait_for_pid(pid))
@@ -8035,6 +8120,20 @@  static int setgid_create_idmapped(void)
 				die("failure: is_setgid");
 		}
 
+		/* create a special file via mknodat() vfs_create */
+		if (mknodat(open_tree_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0))
+			die("failure: mknodat");
+
+		if (is_setgid(open_tree_fd, FILE2, 0))
+			die("failure: is_setgid");
+
+		/* create a whiteout device via mknodat() vfs_mknod */
+		if (mknodat(open_tree_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, 0))
+			die("failure: mknodat");
+
+		if (is_setgid(open_tree_fd, CHRDEV1, 0))
+			die("failure: is_setgid");
+
 		/*
 		 * In setgid directories newly created files always inherit the
 		 * gid from the parent directory. Verify that the file is owned
@@ -8051,6 +8150,24 @@  static int setgid_create_idmapped(void)
 		if (!expected_uid_gid(open_tree_fd, DIR1, 0, 10000, 10000))
 			die("failure: check ownership");
 
+		if (!expected_uid_gid(open_tree_fd, FILE2, 0, 10000, 10000))
+			die("failure: check ownership");
+
+		if (!expected_uid_gid(open_tree_fd, CHRDEV1, 0, 10000, 10000))
+			die("failure: check ownership");
+
+		if (unlinkat(open_tree_fd, FILE1, 0))
+			die("failure: delete");
+
+		if (unlinkat(open_tree_fd, DIR1, AT_REMOVEDIR))
+			die("failure: delete");
+
+		if (unlinkat(open_tree_fd, FILE2, 0))
+			die("failure: delete");
+
+		if (unlinkat(open_tree_fd, CHRDEV1, 0))
+			die("failure: delete");
+
 		exit(EXIT_SUCCESS);
 	}
 	if (wait_for_pid(pid))
@@ -8149,18 +8266,44 @@  static int setgid_create_idmapped_in_userns(void)
 		if (!is_setgid(open_tree_fd, DIR1, 0))
 			die("failure: is_setgid");
 
+		/* create a special file via mknodat() vfs_create */
+		if (mknodat(open_tree_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0))
+			die("failure: mknodat");
+
+		if (!is_setgid(open_tree_fd, FILE2, 0))
+			die("failure: is_setgid");
+
+		/* create a whiteout device via mknodat() vfs_mknod */
+		if (mknodat(open_tree_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, 0))
+			die("failure: mknodat");
+
+		if (!is_setgid(open_tree_fd, CHRDEV1, 0))
+			die("failure: is_setgid");
+
 		if (!expected_uid_gid(open_tree_fd, FILE1, 0, 0, 0))
 			die("failure: check ownership");
 
 		if (!expected_uid_gid(open_tree_fd, DIR1, 0, 0, 0))
 			die("failure: check ownership");
 
+		if (!expected_uid_gid(open_tree_fd, FILE2, 0, 0, 0))
+			die("failure: check ownership");
+
+		if (!expected_uid_gid(open_tree_fd, CHRDEV1, 0, 0, 0))
+			die("failure: check ownership");
+
 		if (unlinkat(open_tree_fd, FILE1, 0))
 			die("failure: delete");
 
 		if (unlinkat(open_tree_fd, DIR1, AT_REMOVEDIR))
 			die("failure: delete");
 
+		if (unlinkat(open_tree_fd, FILE2, 0))
+			die("failure: delete");
+
+		if (unlinkat(open_tree_fd, CHRDEV1, 0))
+			die("failure: delete");
+
 		exit(EXIT_SUCCESS);
 	}
 	if (wait_for_pid(pid))
@@ -8190,9 +8333,12 @@  static int setgid_create_idmapped_in_userns(void)
 			exit(EXIT_SUCCESS);
 		}
 
-		if (!switch_userns(attr.userns_fd, 0, 0, true))
+		if (!switch_userns(attr.userns_fd, 0, 0, false))
 			die("failure: switch_userns");
 
+		if (!caps_down_fsetid())
+			die("failure: caps_down_fsetid");
+
 		/* create regular file via open() */
 		file1_fd = openat(open_tree_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID);
 		if (file1_fd < 0)
@@ -8218,6 +8364,20 @@  static int setgid_create_idmapped_in_userns(void)
 				die("failure: is_setgid");
 		}
 
+		/* create a special file via mknodat() vfs_create */
+		if (mknodat(open_tree_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0))
+			die("failure: mknodat");
+
+		if (is_setgid(open_tree_fd, FILE2, 0))
+			die("failure: is_setgid");
+
+		/* create a whiteout device via mknodat() vfs_mknod */
+		if (mknodat(open_tree_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, 0))
+			die("failure: mknodat");
+
+		if (is_setgid(open_tree_fd, CHRDEV1, 0))
+			die("failure: is_setgid");
+
 		/*
 		 * In setgid directories newly created files always inherit the
 		 * gid from the parent directory. Verify that the file is owned
@@ -8234,12 +8394,24 @@  static int setgid_create_idmapped_in_userns(void)
 		if (!expected_uid_gid(open_tree_fd, DIR1, 0, 0, 1000))
 			die("failure: check ownership");
 
+		if (!expected_uid_gid(open_tree_fd, FILE2, 0, 0, 1000))
+			die("failure: check ownership");
+
+		if (!expected_uid_gid(open_tree_fd, CHRDEV1, 0, 0, 1000))
+			die("failure: check ownership");
+
 		if (unlinkat(open_tree_fd, FILE1, 0))
 			die("failure: delete");
 
 		if (unlinkat(open_tree_fd, DIR1, AT_REMOVEDIR))
 			die("failure: delete");
 
+		if (unlinkat(open_tree_fd, FILE2, 0))
+			die("failure: delete");
+
+		if (unlinkat(open_tree_fd, CHRDEV1, 0))
+			die("failure: delete");
+
 		exit(EXIT_SUCCESS);
 	}
 	if (wait_for_pid(pid))
@@ -8266,9 +8438,12 @@  static int setgid_create_idmapped_in_userns(void)
 			exit(EXIT_SUCCESS);
 		}
 
-		if (!switch_userns(attr.userns_fd, 0, 1000, true))
+		if (!switch_userns(attr.userns_fd, 0, 1000, false))
 			die("failure: switch_userns");
 
+		if (!caps_down_fsetid())
+			die("failure: caps_down_fsetid");
+
 		/* create regular file via open() */
 		file1_fd = openat(open_tree_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID);
 		if (file1_fd < 0)
@@ -8295,12 +8470,44 @@  static int setgid_create_idmapped_in_userns(void)
 				die("failure: is_setgid");
 		}
 
+		/* create a special file via mknodat() vfs_create */
+		if (mknodat(open_tree_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0))
+			die("failure: mknodat");
+
+		if (is_setgid(open_tree_fd, FILE2, 0))
+			die("failure: is_setgid");
+
+		/* create a whiteout device via mknodat() vfs_mknod */
+		if (mknodat(open_tree_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, 0))
+			die("failure: mknodat");
+
+		if (is_setgid(open_tree_fd, CHRDEV1, 0))
+			die("failure: is_setgid");
+
 		if (!expected_uid_gid(open_tree_fd, FILE1, 0, 0, 0))
 			die("failure: check ownership");
 
 		if (!expected_uid_gid(open_tree_fd, DIR1, 0, 0, 0))
 			die("failure: check ownership");
 
+		if (!expected_uid_gid(open_tree_fd, FILE2, 0, 0, 0))
+			die("failure: check ownership");
+
+		if (!expected_uid_gid(open_tree_fd, CHRDEV1, 0, 0, 0))
+			die("failure: check ownership");
+
+		if (unlinkat(open_tree_fd, FILE1, 0))
+			die("failure: delete");
+
+		if (unlinkat(open_tree_fd, DIR1, AT_REMOVEDIR))
+			die("failure: delete");
+
+		if (unlinkat(open_tree_fd, FILE2, 0))
+			die("failure: delete");
+
+		if (unlinkat(open_tree_fd, CHRDEV1, 0))
+			die("failure: delete");
+
 		exit(EXIT_SUCCESS);
 	}
 	if (wait_for_pid(pid))