From: Christian Brauner <christian.brauner(a)ubuntu.com>
Ceph always inherits the SGID bit if it is set on the parent inode,
while the generic inode_init_owner does not do this in a few cases where
it can create a possible security problem (cf. [1]).
Update ceph to strip the SGID bit just as inode_init_owner would.
This bug was detected by the mapped mount testsuite in [3]. The
testsuite tests all core VFS functionality and semantics with and
without mapped mounts. That is to say it functions as a generic VFS
testsuite in addition to a mapped mount testsuite. While working on
mapped mount support for ceph, SIGD inheritance was the only failing
test for ceph after the port.
The same bug was detected by the mapped mount testsuite in XFS in
January 2021 (cf. [2]).
[1]: commit 0fa3ecd87848 ("Fix up non-directory creation in SGID directories")
[2]: commit 01ea173e103e ("xfs: fix up non-directory creation in SGID directories")
[3]: https://git.kernel.org/fs/xfs/xfstests-dev.git
Cc: Ilya Dryomov <idryomov(a)gmail.com>
Cc: Christoph Hellwig <hch(a)lst.de>
Cc: Jeff Layton <jlayton(a)kernel.org>
Cc: ceph-devel(a)vger.kernel.org
CC: linux-fsdevel(a)vger.kernel.org
Cc: stable(a)vger.kernel.org
Signed-off-by: Christian Brauner <christian.brauner(a)ubuntu.com>
---
The test used for this is [3]:
/* 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
* setgid bit if the caller is in the group of the directory.
*/
static int setgid_create(void)
{
int fret = -1;
int file1_fd = -EBADF;
pid_t pid;
if (!caps_supported())
return 0;
if (fchmod(t_dir1_fd, S_IRUSR |
S_IWUSR |
S_IRGRP |
S_IWGRP |
S_IROTH |
S_IWOTH |
S_IXUSR |
S_IXGRP |
S_IXOTH |
S_ISGID), 0) {
log_stderr("failure: fchmod");
goto out;
}
/* Verify that the setgid bit got raised. */
if (!is_setgid(t_dir1_fd, "", AT_EMPTY_PATH)) {
log_stderr("failure: is_setgid");
goto out;
}
pid = fork();
if (pid < 0) {
log_stderr("failure: fork");
goto out;
}
if (pid == 0) {
/* create regular file via open() */
file1_fd = openat(t_dir1_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID);
if (file1_fd < 0)
die("failure: create");
/* We're capable_wrt_inode_uidgid() and also our fsgid matches
* the directories gid.
*/
if (!is_setgid(t_dir1_fd, FILE1, 0))
die("failure: is_setgid");
/* create directory */
if (mkdirat(t_dir1_fd, DIR1, 0000))
die("failure: create");
/* Directories always inherit the setgid bit. */
if (!is_setgid(t_dir1_fd, DIR1, 0))
die("failure: is_setgid");
if (unlinkat(t_dir1_fd, FILE1, 0))
die("failure: delete");
if (unlinkat(t_dir1_fd, DIR1, AT_REMOVEDIR))
die("failure: delete");
exit(EXIT_SUCCESS);
}
if (wait_for_pid(pid))
goto out;
pid = fork();
if (pid < 0) {
log_stderr("failure: fork");
goto out;
}
if (pid == 0) {
if (!switch_ids(0, 10000))
die("failure: switch_ids");
if (!caps_down())
die("failure: caps_down");
/* create regular file via open() */
file1_fd = openat(t_dir1_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID);
if (file1_fd < 0)
die("failure: create");
/* Neither in_group_p() nor capable_wrt_inode_uidgid() so setgid
* bit needs to be stripped.
*/
if (is_setgid(t_dir1_fd, FILE1, 0))
die("failure: is_setgid");
/* create directory */
if (mkdirat(t_dir1_fd, DIR1, 0000))
die("failure: create");
if (xfs_irix_sgid_inherit_enabled()) {
/* We're not in_group_p(). */
if (is_setgid(t_dir1_fd, DIR1, 0))
die("failure: is_setgid");
} else {
/* Directories always inherit the setgid bit. */
if (!is_setgid(t_dir1_fd, DIR1, 0))
die("failure: is_setgid");
}
exit(EXIT_SUCCESS);
}
if (wait_for_pid(pid))
goto out;
fret = 0;
log_debug("Ran test");
out:
safe_close(file1_fd);
return fret;
}
---
fs/ceph/file.c | 18 +++++++++++++++---
1 file changed, 15 insertions(+), 3 deletions(-)
diff --git a/fs/ceph/file.c b/fs/ceph/file.c
index 02a0a0fd9ccd..d9236b2c7a43 100644
--- a/fs/ceph/file.c
+++ b/fs/ceph/file.c
@@ -605,13 +605,25 @@ static int ceph_finish_async_create(struct inode *dir, struct dentry *dentry,
in.cap.realm = cpu_to_le64(ci->i_snap_realm->ino);
in.cap.flags = CEPH_CAP_FLAG_AUTH;
in.ctime = in.mtime = in.atime = iinfo.btime;
- in.mode = cpu_to_le32((u32)mode);
in.truncate_seq = cpu_to_le32(1);
in.truncate_size = cpu_to_le64(-1ULL);
in.xattr_version = cpu_to_le64(1);
in.uid = cpu_to_le32(from_kuid(&init_user_ns, current_fsuid()));
- in.gid = cpu_to_le32(from_kgid(&init_user_ns, dir->i_mode & S_ISGID ?
- dir->i_gid : current_fsgid()));
+ if (dir->i_mode & S_ISGID) {
+ in.gid = from_kgid(&init_user_ns, dir->i_gid);
+
+ /* Directories always inherit the setgid bit. */
+ if (S_ISDIR(mode))
+ mode |= S_ISGID;
+ else if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP) &&
+ !in_group_p(dir->i_gid) &&
+ !capable_wrt_inode_uidgid(&init_user_ns, dir, CAP_FSETID))
+ mode &= ~S_ISGID;
+ } else {
+ in.gid = from_kgid(&init_user_ns, current_fsgid());
+ }
+ in.mode = cpu_to_le32((u32)mode);
+
in.nlink = cpu_to_le32(1);
in.max_size = cpu_to_le64(lo->stripe_unit);
base-commit: 136057256686de39cc3a07c2e39ef6bc43003ff6
--
2.30.2
From: Weichao Guo <guoweichao(a)oppo.com>
[ Upstream commit 6663b138ded1a59e630c9e605e42aa7fde490cdc ]
Inconsistent node block will cause a file fail to open or read,
which could make the user process crashes or stucks. Let's mark
SBI_NEED_FSCK flag to trigger a fix at next fsck time. After
unlinking the corrupted file, the user process could regenerate
a new one and work correctly.
Signed-off-by: Weichao Guo <guoweichao(a)oppo.com>
Reviewed-by: Chao Yu <chao(a)kernel.org>
Signed-off-by: Jaegeuk Kim <jaegeuk(a)kernel.org>
Signed-off-by: Sasha Levin <sashal(a)kernel.org>
---
fs/f2fs/node.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
index 597a145c08ef5..7e625806bd4a2 100644
--- a/fs/f2fs/node.c
+++ b/fs/f2fs/node.c
@@ -1389,6 +1389,7 @@ static struct page *__get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid,
nid, nid_of_node(page), ino_of_node(page),
ofs_of_node(page), cpver_of_node(page),
next_blkaddr_of_node(page));
+ set_sbi_flag(sbi, SBI_NEED_FSCK);
err = -EINVAL;
out_err:
ClearPageUptodate(page);
--
2.33.0
From: Zekun Shen <bruceshenzk(a)gmail.com>
[ Upstream commit b922f622592af76b57cbc566eaeccda0b31a3496 ]
This bug report shows up when running our research tools. The
reports is SOOB read, but it seems SOOB write is also possible
a few lines below.
In details, fw.len and sw.len are inputs coming from io. A len
over the size of self->rpc triggers SOOB. The patch fixes the
bugs by adding sanity checks.
The bugs are triggerable with compromised/malfunctioning devices.
They are potentially exploitable given they first leak up to
0xffff bytes and able to overwrite the region later.
The patch is tested with QEMU emulater.
This is NOT tested with a real device.
Attached is the log we found by fuzzing.
BUG: KASAN: slab-out-of-bounds in
hw_atl_utils_fw_upload_dwords+0x393/0x3c0 [atlantic]
Read of size 4 at addr ffff888016260b08 by task modprobe/213
CPU: 0 PID: 213 Comm: modprobe Not tainted 5.6.0 #1
Call Trace:
dump_stack+0x76/0xa0
print_address_description.constprop.0+0x16/0x200
? hw_atl_utils_fw_upload_dwords+0x393/0x3c0 [atlantic]
? hw_atl_utils_fw_upload_dwords+0x393/0x3c0 [atlantic]
__kasan_report.cold+0x37/0x7c
? aq_hw_read_reg_bit+0x60/0x70 [atlantic]
? hw_atl_utils_fw_upload_dwords+0x393/0x3c0 [atlantic]
kasan_report+0xe/0x20
hw_atl_utils_fw_upload_dwords+0x393/0x3c0 [atlantic]
hw_atl_utils_fw_rpc_call+0x95/0x130 [atlantic]
hw_atl_utils_fw_rpc_wait+0x176/0x210 [atlantic]
hw_atl_utils_mpi_create+0x229/0x2e0 [atlantic]
? hw_atl_utils_fw_rpc_wait+0x210/0x210 [atlantic]
? hw_atl_utils_initfw+0x9f/0x1c8 [atlantic]
hw_atl_utils_initfw+0x12a/0x1c8 [atlantic]
aq_nic_ndev_register+0x88/0x650 [atlantic]
? aq_nic_ndev_init+0x235/0x3c0 [atlantic]
aq_pci_probe+0x731/0x9b0 [atlantic]
? aq_pci_func_init+0xc0/0xc0 [atlantic]
local_pci_probe+0xd3/0x160
pci_device_probe+0x23f/0x3e0
Reported-by: Brendan Dolan-Gavitt <brendandg(a)nyu.edu>
Signed-off-by: Zekun Shen <bruceshenzk(a)gmail.com>
Signed-off-by: David S. Miller <davem(a)davemloft.net>
Signed-off-by: Sasha Levin <sashal(a)kernel.org>
---
.../ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c
index 1fe016fc4bc70..5a7a1fd44d3fa 100644
--- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c
+++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c
@@ -209,6 +209,11 @@ static int hw_atl_utils_fw_rpc_wait(struct aq_hw_s *self,
goto err_exit;
if (fw.len == 0xFFFFU) {
+ if (sw.len > sizeof(self->rpc)) {
+ printk(KERN_INFO "Invalid sw len: %x\n", sw.len);
+ err = -EINVAL;
+ goto err_exit;
+ }
err = hw_atl_utils_fw_rpc_call(self, sw.len);
if (err < 0)
goto err_exit;
@@ -219,6 +224,11 @@ static int hw_atl_utils_fw_rpc_wait(struct aq_hw_s *self,
if (rpc) {
if (fw.len) {
+ if (fw.len > sizeof(self->rpc)) {
+ printk(KERN_INFO "Invalid fw len: %x\n", fw.len);
+ err = -EINVAL;
+ goto err_exit;
+ }
err =
hw_atl_utils_fw_downld_dwords(self,
PHAL_ATLANTIC->rpc_addr,
--
2.33.0
From: Andreas Gruenbacher <agruenba(a)redhat.com>
[ Upstream commit f3506eee81d1f700d9ee2d2f4a88fddb669ec032 ]
Fix the length of holes reported at the end of a file: the length is
relative to the beginning of the extent, not the seek position which is
rounded down to the filesystem block size.
This bug went unnoticed for some time, but is now caught by the
following assertion in iomap_iter_done():
WARN_ON_ONCE(iter->iomap.offset + iter->iomap.length <= iter->pos)
Signed-off-by: Andreas Gruenbacher <agruenba(a)redhat.com>
Signed-off-by: Sasha Levin <sashal(a)kernel.org>
---
fs/gfs2/bmap.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index 43f53020553b5..53ba5019ad063 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -943,7 +943,7 @@ static int gfs2_iomap_get(struct inode *inode, loff_t pos, loff_t length,
else if (height == ip->i_height)
ret = gfs2_hole_size(inode, lblock, len, mp, iomap);
else
- iomap->length = size - pos;
+ iomap->length = size - iomap->offset;
} else if (flags & IOMAP_WRITE) {
u64 alloc_size;
--
2.33.0
From: Andreas Gruenbacher <agruenba(a)redhat.com>
[ Upstream commit f3506eee81d1f700d9ee2d2f4a88fddb669ec032 ]
Fix the length of holes reported at the end of a file: the length is
relative to the beginning of the extent, not the seek position which is
rounded down to the filesystem block size.
This bug went unnoticed for some time, but is now caught by the
following assertion in iomap_iter_done():
WARN_ON_ONCE(iter->iomap.offset + iter->iomap.length <= iter->pos)
Signed-off-by: Andreas Gruenbacher <agruenba(a)redhat.com>
Signed-off-by: Sasha Levin <sashal(a)kernel.org>
---
fs/gfs2/bmap.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index aaec3c5b02028..dec5285a02e9d 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -940,7 +940,7 @@ static int gfs2_iomap_get(struct inode *inode, loff_t pos, loff_t length,
else if (height == ip->i_height)
ret = gfs2_hole_size(inode, lblock, len, mp, iomap);
else
- iomap->length = size - pos;
+ iomap->length = size - iomap->offset;
} else if (flags & IOMAP_WRITE) {
u64 alloc_size;
--
2.33.0