From: Miklos Szeredi <mszeredi(a)redhat.com>
commit 4a2abf99f9c2877039006b8dfb3ec4e0d1278fe8 upstream
In the FOPEN_DIRECT_IO case the write path doesn't call file_remove_privs()
and that means setuid bit is not cleared if unpriviliged user writes to a
file with setuid bit set.
pjdfstest chmod test 12.t tests this and fails.
Fix this by adding a flag to the FUSE_WRITE message that requests clearing
privileges on the given file. This needs
This better than just calling fuse_remove_privs(), because the attributes
may not be up to date, so in that case a write may miss clearing the
privileges.
Test case:
$ passthrough_ll /mnt/pasthrough-mnt -o default_permissions,allow_other,cache=never
$ mkdir /mnt/pasthrough-mnt/testdir
$ cd /mnt/pasthrough-mnt/testdir
$ prove -rv pjdfstests/tests/chmod/12.t
Reported-by: Vivek Goyal <vgoyal(a)redhat.com>
Signed-off-by: Miklos Szeredi <mszeredi(a)redhat.com>
Tested-by: Vivek Goyal <vgoyal(a)redhat.com>
Signed-off-by: Jeffle Xu <jefflexu(a)linux.alibaba.com>
---
Corresponding virtiofsd commits can refer to
[1] virtiofsd: Parse flag FUSE_WRITE_KILL_PRIV
https://gitlab.com/virtio-fs/qemu/-/commit/f779bc5265e7e7abb13a03d4bfbc7415…
[2] virtiofsd: Drop CAP_FSETID if client asked for it
https://gitlab.com/virtio-fs/qemu/-/commit/ee88465224b3aed2596049caa28f86cb…
---
fs/fuse/file.c | 11 +++++++++--
include/uapi/linux/fuse.h | 5 +++++
2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 80f991ac4a0a..e6d2e5e2b072 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -1404,10 +1404,17 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter,
if (err && !nbytes)
break;
- if (write)
+ if (write) {
+ if (!capable(CAP_FSETID)) {
+ struct fuse_write_in *inarg;
+
+ inarg = &req->misc.write.in;
+ inarg->write_flags |= FUSE_WRITE_KILL_PRIV;
+ }
nres = fuse_send_write(req, io, pos, nbytes, owner);
- else
+ } else {
nres = fuse_send_read(req, io, pos, nbytes, owner);
+ }
if (!io->async)
fuse_release_user_pages(req, io->should_dirty);
diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h
index 1c6956b32597..5a011734a97b 100644
--- a/include/uapi/linux/fuse.h
+++ b/include/uapi/linux/fuse.h
@@ -120,6 +120,9 @@
* 7.28
* - add FOPEN_CACHE_DIR
* - add FUSE_CACHE_SYMLINKS
+ *
+ * 7.31
+ * - add FUSE_WRITE_KILL_PRIV flag
*/
#ifndef _LINUX_FUSE_H
@@ -315,9 +318,11 @@ struct fuse_file_lock {
*
* FUSE_WRITE_CACHE: delayed write from page cache, file handle is guessed
* FUSE_WRITE_LOCKOWNER: lock_owner field is valid
+ * FUSE_WRITE_KILL_PRIV: kill suid and sgid bits
*/
#define FUSE_WRITE_CACHE (1 << 0)
#define FUSE_WRITE_LOCKOWNER (1 << 1)
+#define FUSE_WRITE_KILL_PRIV (1 << 2)
/**
* Read flags
--
2.27.0
These two fields were originally introduced for metadata coherency,
and shall be used by [1]. However, this feature doesn't get merged to
upstream, and we didn't backport [1] yet. Thus remove these two stale
fields.
[1] "fuse: add shared version support"
https://www.mail-archive.com/linux-kernel@vger.kernel.org/msg1873773.html
Signed-off-by: Jeffle Xu <jefflexu(a)linux.alibaba.com>
---
fs/fuse/fuse_i.h | 6 ------
1 file changed, 6 deletions(-)
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 83632816b581..c0799cc2249f 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -83,12 +83,6 @@ struct fuse_mount_data {
/* fuse_dev pointer to fill in, should contain NULL on entry */
void **fudptr;
-
- /* version table length in bytes */
- size_t vertab_len;
-
- /* version table kernel address */
- void *vertab_kaddr;
};
/* One forget request */
--
2.27.0