aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
authorMark Brown <broonie@kernel.org>2026-05-29 17:54:34 +0100
committerMark Brown <broonie@kernel.org>2026-05-29 17:54:34 +0100
commit686a7ac3f6b13a5800282ddd81d80d71f29f75d6 (patch)
treea3b650893c5b948bb8a0f93c24d47e2712a9ef8b /fs
parent18a549b897f99757e5ec7ed570222de21c36285f (diff)
parentc60ffec33ddf24577f6f4da18fe825b2058c5f78 (diff)
downloadlinux-next-history-686a7ac3f6b13a5800282ddd81d80d71f29f75d6.tar.gz
Merge branch 'mm-nonmm-stable' of https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
Diffstat (limited to 'fs')
-rw-r--r--fs/Kconfig6
-rw-r--r--fs/btrfs/raid56.c8
-rw-r--r--fs/fat/fat_test.c33
-rw-r--r--fs/ocfs2/file.c23
-rw-r--r--fs/ocfs2/inode.c151
-rw-r--r--fs/ocfs2/journal.c7
-rw-r--r--fs/ocfs2/ocfs2.h2
-rw-r--r--fs/ocfs2/quota_local.c2
-rw-r--r--fs/ocfs2/super.c2
-rw-r--r--fs/ocfs2/sysfile.c9
-rw-r--r--fs/ocfs2/xattr.c123
-rw-r--r--fs/proc/base.c57
-rw-r--r--fs/proc/generic.c10
13 files changed, 323 insertions, 110 deletions
diff --git a/fs/Kconfig b/fs/Kconfig
index 43cb06de297ff..cf6ae64776e62 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -78,7 +78,7 @@ config FS_DAX
--map=mem:
https://docs.pmem.io/ndctl-user-guide/ndctl-man-pages/ndctl-create-namespace
- For ndctl to work CONFIG_DEV_DAX needs to be enabled as well. For most
+ For ndctl to work CONFIG_DEV_DAX needs to be enabled as well. For most
file systems DAX support needs to be manually enabled globally or
per-inode using a mount option as well. See the file documentation in
Documentation/filesystems/dax.rst for details.
@@ -116,8 +116,8 @@ config FILE_LOCKING
default y
help
This option enables standard file locking support, required
- for filesystems like NFS and for the flock() system
- call. Disabling this option saves about 11k.
+ for filesystems like NFS and for the flock() system
+ call. Disabling this option saves about 11k.
source "fs/crypto/Kconfig"
diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c
index 08ee8f316d96d..dabc9522e8814 100644
--- a/fs/btrfs/raid56.c
+++ b/fs/btrfs/raid56.c
@@ -1410,7 +1410,7 @@ static void generate_pq_vertical_step(struct btrfs_raid_bio *rbio, unsigned int
rbio_qstripe_paddr(rbio, sector_nr, step_nr));
assert_rbio(rbio);
- raid6_call.gen_syndrome(rbio->real_stripes, step, pointers);
+ raid6_gen_syndrome(rbio->real_stripes, step, pointers);
} else {
/* raid5 */
memcpy(pointers[rbio->nr_data], pointers[0], step);
@@ -1987,10 +1987,10 @@ static void recover_vertical_step(struct btrfs_raid_bio *rbio,
}
if (failb == rbio->real_stripes - 2) {
- raid6_datap_recov(rbio->real_stripes, step,
+ raid6_recov_datap(rbio->real_stripes, step,
faila, pointers);
} else {
- raid6_2data_recov(rbio->real_stripes, step,
+ raid6_recov_2data(rbio->real_stripes, step,
faila, failb, pointers);
}
} else {
@@ -2644,7 +2644,7 @@ static bool verify_one_parity_step(struct btrfs_raid_bio *rbio,
if (has_qstripe) {
assert_rbio(rbio);
/* RAID6, call the library function to fill in our P/Q. */
- raid6_call.gen_syndrome(rbio->real_stripes, step, pointers);
+ raid6_gen_syndrome(rbio->real_stripes, step, pointers);
} else {
/* RAID5. */
memcpy(pointers[nr_data], pointers[0], step);
diff --git a/fs/fat/fat_test.c b/fs/fat/fat_test.c
index 886bf044a9f1d..4eeed9dca5494 100644
--- a/fs/fat/fat_test.c
+++ b/fs/fat/fat_test.c
@@ -20,6 +20,37 @@ static void fat_checksum_test(struct kunit *test)
KUNIT_EXPECT_EQ(test, fat_checksum("ABCDEFGHA "), (u8)98);
}
+static void fat_clus_to_blknr_test(struct kunit *test)
+{
+ struct msdos_sb_info sbi = {
+ .sec_per_clus = 4,
+ .data_start = 100,
+ };
+
+ KUNIT_EXPECT_EQ(test, (sector_t)100,
+ fat_clus_to_blknr(&sbi, FAT_START_ENT));
+ KUNIT_EXPECT_EQ(test, (sector_t)112, fat_clus_to_blknr(&sbi, 5));
+}
+
+static void fat_get_blknr_offset_test(struct kunit *test)
+{
+ struct msdos_sb_info sbi = {
+ .dir_per_block = 16,
+ .dir_per_block_bits = 4,
+ };
+
+ sector_t blknr;
+ int offset;
+
+ fat_get_blknr_offset(&sbi, 0, &blknr, &offset);
+ KUNIT_EXPECT_EQ(test, (sector_t)0, blknr);
+ KUNIT_EXPECT_EQ(test, 0, offset);
+
+ fat_get_blknr_offset(&sbi, (10 << 4) | 7, &blknr, &offset);
+ KUNIT_EXPECT_EQ(test, (sector_t)10, blknr);
+ KUNIT_EXPECT_EQ(test, 7, offset);
+}
+
struct fat_timestamp_testcase {
const char *name;
struct timespec64 ts;
@@ -341,6 +372,8 @@ static void fat_truncate_atime_test(struct kunit *test)
static struct kunit_case fat_test_cases[] = {
KUNIT_CASE(fat_checksum_test),
+ KUNIT_CASE(fat_clus_to_blknr_test),
+ KUNIT_CASE(fat_get_blknr_offset_test),
KUNIT_CASE_PARAM(fat_time_fat2unix_test, fat_time_gen_params),
KUNIT_CASE_PARAM(fat_time_unix2fat_test, fat_time_gen_params),
KUNIT_CASE_PARAM(fat_time_unix2fat_clamp_test,
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index 7df9921c1a389..d6e977ba65656 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -444,21 +444,26 @@ int ocfs2_truncate_file(struct inode *inode,
struct ocfs2_dinode *fe = NULL;
struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
- /* We trust di_bh because it comes from ocfs2_inode_lock(), which
- * already validated it */
+ /*
+ * On local mounts ocfs2_inode_lock_update() skips the inode
+ * refresh path, so truncation still needs to reject an inode
+ * state that no longer matches di_bh.
+ */
fe = (struct ocfs2_dinode *) di_bh->b_data;
trace_ocfs2_truncate_file((unsigned long long)OCFS2_I(inode)->ip_blkno,
(unsigned long long)le64_to_cpu(fe->i_size),
(unsigned long long)new_i_size);
- mlog_bug_on_msg(le64_to_cpu(fe->i_size) != i_size_read(inode),
- "Inode %llu, inode i_size = %lld != di "
- "i_size = %llu, i_flags = 0x%x\n",
- (unsigned long long)OCFS2_I(inode)->ip_blkno,
- i_size_read(inode),
- (unsigned long long)le64_to_cpu(fe->i_size),
- le32_to_cpu(fe->i_flags));
+ if (unlikely(le64_to_cpu(fe->i_size) != i_size_read(inode))) {
+ status = ocfs2_error(inode->i_sb,
+ "Inode %llu has inconsistent i_size: inode = %lld, dinode = %llu, i_flags = 0x%x\n",
+ (unsigned long long)OCFS2_I(inode)->ip_blkno,
+ i_size_read(inode),
+ (unsigned long long)le64_to_cpu(fe->i_size),
+ le32_to_cpu(fe->i_flags));
+ goto bail;
+ }
if (new_i_size > le64_to_cpu(fe->i_size)) {
trace_ocfs2_truncate_file_error(
diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c
index a510a0eb1adcc..432eac01c1763 100644
--- a/fs/ocfs2/inode.c
+++ b/fs/ocfs2/inode.c
@@ -13,6 +13,7 @@
#include <linux/pagemap.h>
#include <linux/quotaops.h>
#include <linux/iversion.h>
+#include <linux/fs_dirent.h>
#include <asm/byteorder.h>
@@ -64,7 +65,40 @@ static int ocfs2_filecheck_read_inode_block_full(struct inode *inode,
static int ocfs2_filecheck_validate_inode_block(struct super_block *sb,
struct buffer_head *bh);
static int ocfs2_filecheck_repair_inode_block(struct super_block *sb,
- struct buffer_head *bh);
+ struct buffer_head *bh);
+
+static bool ocfs2_valid_inode_mode(umode_t mode)
+{
+ return fs_umode_to_ftype(mode) != FT_UNKNOWN;
+}
+
+static bool ocfs2_dinode_has_unexpected_rdev(struct ocfs2_dinode *di)
+{
+ umode_t mode = le16_to_cpu(di->i_mode);
+
+ if (le32_to_cpu(di->i_flags) & OCFS2_SYSTEM_FL)
+ return false;
+
+ return !S_ISCHR(mode) && !S_ISBLK(mode) && di->id1.dev1.i_rdev != 0;
+}
+
+static bool ocfs2_dinode_has_size_without_clusters(struct super_block *sb,
+ struct ocfs2_dinode *di)
+{
+ umode_t mode = le16_to_cpu(di->i_mode);
+
+ if (le32_to_cpu(di->i_flags) & OCFS2_SYSTEM_FL)
+ return false;
+ if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL)
+ return false;
+ if (!le64_to_cpu(di->i_size) || le32_to_cpu(di->i_clusters))
+ return false;
+
+ if (S_ISDIR(mode))
+ return true;
+
+ return !ocfs2_sparse_alloc(OCFS2_SB(sb)) && S_ISREG(mode);
+}
void ocfs2_set_inode_flags(struct inode *inode)
{
@@ -1494,6 +1528,86 @@ int ocfs2_validate_inode_block(struct super_block *sb,
goto bail;
}
+ /*
+ * Reject dinodes whose i_mode does not name one of the seven
+ * canonical POSIX file types. ocfs2_populate_inode() copies
+ * i_mode verbatim into inode->i_mode and then dispatches via
+ * switch (mode & S_IFMT) to file/dir/symlink/special_file iops;
+ * an unrecognised type falls into ocfs2_special_file_iops with
+ * init_special_inode(), which interprets i_rdev. Constrain the
+ * type here so the dispatch only ever sees a value mkfs.ocfs2 /
+ * VFS can produce.
+ */
+ if (!ocfs2_valid_inode_mode(le16_to_cpu(di->i_mode))) {
+ rc = ocfs2_error(sb,
+ "Invalid dinode #%llu: mode 0%o has unknown file type\n",
+ (unsigned long long)bh->b_blocknr,
+ le16_to_cpu(di->i_mode));
+ goto bail;
+ }
+
+ /*
+ * id1.dev1.i_rdev is the device-number arm of the id1 union and
+ * is only meaningful for character and block device inodes. For
+ * any other regular user-visible file type the on-disk value
+ * must be zero. ocfs2_populate_inode() currently runs
+ *
+ * inode->i_rdev = huge_decode_dev(le64_to_cpu(fe->id1.dev1.i_rdev));
+ *
+ * unconditionally, before the S_IFMT switch decides whether the
+ * inode is a special file. As a result, an i_rdev value present
+ * on a non-device inode is silently published into the in-core
+ * inode; a subsequent forced re-read or in-core mode mutation
+ * (cluster peer with raw write access to the shared LUN,
+ * on-disk corruption, or a separately forged dinode) can then
+ * expose the attacker-controlled device number to
+ * init_special_inode() without ever showing an unusual i_mode
+ * at validation time.
+ *
+ * System inodes (OCFS2_SYSTEM_FL) legitimately use the bitmap1
+ * and journal1 arms of the same union (allocator i_used /
+ * i_total counters and the journal ij_flags /
+ * ij_recovery_generation pair); those bytes are not an i_rdev
+ * and must not be checked here. Restrict the cross-check to
+ * non-system inodes, which is the full attacker-controllable
+ * surface.
+ */
+ if (ocfs2_dinode_has_unexpected_rdev(di)) {
+ rc = ocfs2_error(sb,
+ "Invalid dinode #%llu: non-device mode 0%o with i_rdev %llu\n",
+ (unsigned long long)bh->b_blocknr,
+ le16_to_cpu(di->i_mode),
+ (unsigned long long)le64_to_cpu(di->id1.dev1.i_rdev));
+ goto bail;
+ }
+
+ /*
+ * Non-inline directories must not have i_size without allocated
+ * clusters: directory growth adds storage before advancing i_size,
+ * and readdir walks i_size block-by-block. A forged directory
+ * with zero clusters and a huge i_size would repeatedly fault on
+ * holes while advancing through the claimed size.
+ *
+ * Non-inline regular files have the same invariant on non-sparse
+ * volumes. Sparse regular files are different: truncate can
+ * legitimately grow i_size without allocating clusters, so keep
+ * the sparse-alloc carveout for S_IFREG only. System inodes and
+ * inline-data dinodes have their own storage rules.
+ */
+ if (ocfs2_dinode_has_size_without_clusters(sb, di)) {
+ if (S_ISDIR(le16_to_cpu(di->i_mode)))
+ rc = ocfs2_error(sb,
+ "Invalid dinode #%llu: directory i_size %llu with i_clusters 0 and no inline-data flag\n",
+ (unsigned long long)bh->b_blocknr,
+ (unsigned long long)le64_to_cpu(di->i_size));
+ else
+ rc = ocfs2_error(sb,
+ "Invalid dinode #%llu: regular file i_size %llu with i_clusters 0 and no inline-data flag on non-sparse volume\n",
+ (unsigned long long)bh->b_blocknr,
+ (unsigned long long)le64_to_cpu(di->i_size));
+ goto bail;
+ }
+
if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL) {
struct ocfs2_inline_data *data = &di->id2.i_data;
@@ -1624,6 +1738,40 @@ static int ocfs2_filecheck_validate_inode_block(struct super_block *sb,
(unsigned long long)bh->b_blocknr,
le32_to_cpu(di->i_fs_generation));
rc = -OCFS2_FILECHECK_ERR_GENERATION;
+ goto bail;
+ }
+
+ if (!ocfs2_valid_inode_mode(le16_to_cpu(di->i_mode))) {
+ mlog(ML_ERROR,
+ "Filecheck: invalid dinode #%llu: mode 0%o has unknown file type\n",
+ (unsigned long long)bh->b_blocknr,
+ le16_to_cpu(di->i_mode));
+ rc = -OCFS2_FILECHECK_ERR_INVALIDINO;
+ goto bail;
+ }
+
+ if (ocfs2_dinode_has_unexpected_rdev(di)) {
+ mlog(ML_ERROR,
+ "Filecheck: invalid dinode #%llu: non-device mode 0%o with i_rdev %llu\n",
+ (unsigned long long)bh->b_blocknr,
+ le16_to_cpu(di->i_mode),
+ (unsigned long long)le64_to_cpu(di->id1.dev1.i_rdev));
+ rc = -OCFS2_FILECHECK_ERR_INVALIDINO;
+ goto bail;
+ }
+
+ if (ocfs2_dinode_has_size_without_clusters(sb, di)) {
+ if (S_ISDIR(le16_to_cpu(di->i_mode)))
+ mlog(ML_ERROR,
+ "Filecheck: invalid dinode #%llu: directory i_size %llu with i_clusters 0 and no inline-data flag\n",
+ (unsigned long long)bh->b_blocknr,
+ (unsigned long long)le64_to_cpu(di->i_size));
+ else
+ mlog(ML_ERROR,
+ "Filecheck: invalid dinode #%llu: regular file i_size %llu with i_clusters 0 and no inline-data flag on non-sparse volume\n",
+ (unsigned long long)bh->b_blocknr,
+ (unsigned long long)le64_to_cpu(di->i_size));
+ rc = -OCFS2_FILECHECK_ERR_INVALIDINO;
}
bail:
@@ -1812,4 +1960,3 @@ const struct ocfs2_caching_operations ocfs2_inode_caching_ops = {
.co_io_lock = ocfs2_inode_cache_io_lock,
.co_io_unlock = ocfs2_inode_cache_io_unlock,
};
-
diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c
index f9bf3bac085db..fc54cc798ce35 100644
--- a/fs/ocfs2/journal.c
+++ b/fs/ocfs2/journal.c
@@ -1022,11 +1022,8 @@ static int ocfs2_journal_toggle_dirty(struct ocfs2_super *osb,
struct ocfs2_dinode *fe;
fe = (struct ocfs2_dinode *)bh->b_data;
-
- /* The journal bh on the osb always comes from ocfs2_journal_init()
- * and was validated there inside ocfs2_inode_lock_full(). It's a
- * code bug if we mess it up. */
- BUG_ON(!OCFS2_IS_VALID_DINODE(fe));
+ if (WARN_ON(!OCFS2_IS_VALID_DINODE(fe)))
+ return -EIO;
flags = le32_to_cpu(fe->id1.journal1.ij_flags);
if (dirty)
diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h
index 7b50e03dfa664..62cad6522c7a3 100644
--- a/fs/ocfs2/ocfs2.h
+++ b/fs/ocfs2/ocfs2.h
@@ -494,8 +494,6 @@ struct ocfs2_super
struct rb_root osb_rf_lock_tree;
struct ocfs2_refcount_tree *osb_ref_tree_lru;
- struct mutex system_file_mutex;
-
/*
* OCFS2 needs to schedule several different types of work which
* require cluster locking, disk I/O, recovery waits, etc. Since these
diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c
index 12cbb4fccda0d..f55810c59b1b1 100644
--- a/fs/ocfs2/quota_local.c
+++ b/fs/ocfs2/quota_local.c
@@ -302,7 +302,7 @@ static int ocfs2_add_recovery_chunk(struct super_block *sb,
if (!rc)
return -ENOMEM;
rc->rc_chunk = chunk;
- rc->rc_bitmap = kmalloc(sb->s_blocksize, GFP_NOFS);
+ rc->rc_bitmap = kzalloc(sb->s_blocksize, GFP_NOFS);
if (!rc->rc_bitmap) {
kfree(rc);
return -ENOMEM;
diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c
index b875f01c97564..6dd45c2153f88 100644
--- a/fs/ocfs2/super.c
+++ b/fs/ocfs2/super.c
@@ -1997,8 +1997,6 @@ static int ocfs2_initialize_super(struct super_block *sb,
spin_lock_init(&osb->osb_xattr_lock);
ocfs2_init_steal_slots(osb);
- mutex_init(&osb->system_file_mutex);
-
atomic_set(&osb->alloc_stats.moves, 0);
atomic_set(&osb->alloc_stats.local_data, 0);
atomic_set(&osb->alloc_stats.bitmap_data, 0);
diff --git a/fs/ocfs2/sysfile.c b/fs/ocfs2/sysfile.c
index d53a6cc866bef..67e492f4b828b 100644
--- a/fs/ocfs2/sysfile.c
+++ b/fs/ocfs2/sysfile.c
@@ -98,11 +98,9 @@ struct inode *ocfs2_get_system_file_inode(struct ocfs2_super *osb,
} else
arr = get_local_system_inode(osb, type, slot);
- mutex_lock(&osb->system_file_mutex);
if (arr && ((inode = *arr) != NULL)) {
/* get a ref in addition to the array ref */
inode = igrab(inode);
- mutex_unlock(&osb->system_file_mutex);
BUG_ON(!inode);
return inode;
@@ -112,11 +110,10 @@ struct inode *ocfs2_get_system_file_inode(struct ocfs2_super *osb,
inode = _ocfs2_get_system_file_inode(osb, type, slot);
/* add one more if putting into array for first time */
- if (arr && inode) {
- *arr = igrab(inode);
- BUG_ON(!*arr);
+ if (inode && arr && !*arr && !cmpxchg(&(*arr), NULL, inode)) {
+ inode = igrab(inode);
+ BUG_ON(!inode);
}
- mutex_unlock(&osb->system_file_mutex);
return inode;
}
diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
index 86cfd4c2adf92..fcddd3c13acdd 100644
--- a/fs/ocfs2/xattr.c
+++ b/fs/ocfs2/xattr.c
@@ -950,15 +950,51 @@ static int ocfs2_xattr_list_entries(struct inode *inode,
return result;
}
+static int ocfs2_xattr_ibody_lookup_header(struct inode *inode,
+ struct ocfs2_dinode *di,
+ struct ocfs2_xattr_header **header)
+{
+ u16 xattr_count;
+ size_t max_entries;
+ u16 inline_size = le16_to_cpu(di->i_xattr_inline_size);
+
+ if (inline_size > inode->i_sb->s_blocksize ||
+ inline_size < sizeof(struct ocfs2_xattr_header)) {
+ ocfs2_error(inode->i_sb,
+ "Invalid xattr inline size %u in inode %llu\n",
+ inline_size,
+ (unsigned long long)OCFS2_I(inode)->ip_blkno);
+ return -EFSCORRUPTED;
+ }
+
+ *header = (struct ocfs2_xattr_header *)
+ ((void *)di + inode->i_sb->s_blocksize - inline_size);
+
+ xattr_count = le16_to_cpu((*header)->xh_count);
+ max_entries = (inline_size - sizeof(struct ocfs2_xattr_header)) /
+ sizeof(struct ocfs2_xattr_entry);
+
+ if (xattr_count > max_entries) {
+ ocfs2_error(inode->i_sb,
+ "xattr entry count %u exceeds maximum %zu in inode %llu\n",
+ xattr_count, max_entries,
+ (unsigned long long)OCFS2_I(inode)->ip_blkno);
+ return -EFSCORRUPTED;
+ }
+
+ return 0;
+}
+
int ocfs2_has_inline_xattr_value_outside(struct inode *inode,
struct ocfs2_dinode *di)
{
struct ocfs2_xattr_header *xh;
+ int ret;
int i;
- xh = (struct ocfs2_xattr_header *)
- ((void *)di + inode->i_sb->s_blocksize -
- le16_to_cpu(di->i_xattr_inline_size));
+ ret = ocfs2_xattr_ibody_lookup_header(inode, di, &xh);
+ if (ret)
+ return 1;
for (i = 0; i < le16_to_cpu(xh->xh_count); i++)
if (!ocfs2_xattr_is_local(&xh->xh_entries[i]))
@@ -975,39 +1011,13 @@ static int ocfs2_xattr_ibody_list(struct inode *inode,
struct ocfs2_xattr_header *header = NULL;
struct ocfs2_inode_info *oi = OCFS2_I(inode);
int ret = 0;
- u16 xattr_count;
- size_t max_entries;
- u16 inline_size;
if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL))
return ret;
- inline_size = le16_to_cpu(di->i_xattr_inline_size);
-
- /* Validate inline size is reasonable */
- if (inline_size > inode->i_sb->s_blocksize ||
- inline_size < sizeof(struct ocfs2_xattr_header)) {
- ocfs2_error(inode->i_sb,
- "Invalid xattr inline size %u in inode %llu\n",
- inline_size,
- (unsigned long long)OCFS2_I(inode)->ip_blkno);
- return -EFSCORRUPTED;
- }
-
- header = (struct ocfs2_xattr_header *)
- ((void *)di + inode->i_sb->s_blocksize - inline_size);
-
- xattr_count = le16_to_cpu(header->xh_count);
- max_entries = (inline_size - sizeof(struct ocfs2_xattr_header)) /
- sizeof(struct ocfs2_xattr_entry);
-
- if (xattr_count > max_entries) {
- ocfs2_error(inode->i_sb,
- "xattr entry count %u exceeds maximum %zu in inode %llu\n",
- xattr_count, max_entries,
- (unsigned long long)OCFS2_I(inode)->ip_blkno);
- return -EFSCORRUPTED;
- }
+ ret = ocfs2_xattr_ibody_lookup_header(inode, di, &header);
+ if (ret)
+ return ret;
ret = ocfs2_xattr_list_entries(inode, header, buffer, buffer_size);
@@ -1200,8 +1210,9 @@ static int ocfs2_xattr_ibody_get(struct inode *inode,
return -ENODATA;
xs->end = (void *)di + inode->i_sb->s_blocksize;
- xs->header = (struct ocfs2_xattr_header *)
- (xs->end - le16_to_cpu(di->i_xattr_inline_size));
+ ret = ocfs2_xattr_ibody_lookup_header(inode, di, &xs->header);
+ if (ret)
+ return ret;
xs->base = (void *)xs->header;
xs->here = xs->header->xh_entries;
@@ -2465,9 +2476,9 @@ static int ocfs2_xattr_ibody_remove(struct inode *inode,
.vb_access = ocfs2_journal_access_di,
};
- header = (struct ocfs2_xattr_header *)
- ((void *)di + inode->i_sb->s_blocksize -
- le16_to_cpu(di->i_xattr_inline_size));
+ ret = ocfs2_xattr_ibody_lookup_header(inode, di, &header);
+ if (ret)
+ return ret;
ret = ocfs2_remove_value_outside(inode, &vb, header,
ref_ci, ref_root_bh);
@@ -2726,12 +2737,14 @@ static int ocfs2_xattr_ibody_find(struct inode *inode,
xs->xattr_bh = xs->inode_bh;
xs->end = (void *)di + inode->i_sb->s_blocksize;
- if (oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL)
- xs->header = (struct ocfs2_xattr_header *)
- (xs->end - le16_to_cpu(di->i_xattr_inline_size));
- else
+ if (oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL) {
+ ret = ocfs2_xattr_ibody_lookup_header(inode, di, &xs->header);
+ if (ret)
+ return ret;
+ } else {
xs->header = (struct ocfs2_xattr_header *)
(xs->end - OCFS2_SB(inode->i_sb)->s_xattr_inline_size);
+ }
xs->base = (void *)xs->header;
xs->here = xs->header->xh_entries;
@@ -6003,14 +6016,17 @@ static int ocfs2_xattr_inline_attach_refcount(struct inode *inode,
struct ocfs2_cached_dealloc_ctxt *dealloc)
{
struct ocfs2_dinode *di = (struct ocfs2_dinode *)fe_bh->b_data;
- struct ocfs2_xattr_header *header = (struct ocfs2_xattr_header *)
- (fe_bh->b_data + inode->i_sb->s_blocksize -
- le16_to_cpu(di->i_xattr_inline_size));
+ struct ocfs2_xattr_header *header;
+ int ret;
struct ocfs2_xattr_value_buf vb = {
.vb_bh = fe_bh,
.vb_access = ocfs2_journal_access_di,
};
+ ret = ocfs2_xattr_ibody_lookup_header(inode, di, &header);
+ if (ret)
+ return ret;
+
return ocfs2_xattr_attach_refcount_normal(inode, &vb, header,
ref_ci, ref_root_bh, dealloc);
}
@@ -6495,12 +6511,10 @@ static int ocfs2_reflink_xattr_inline(struct ocfs2_xattr_reflink *args)
handle_t *handle;
struct ocfs2_super *osb = OCFS2_SB(args->old_inode->i_sb);
struct ocfs2_dinode *di = (struct ocfs2_dinode *)args->old_bh->b_data;
- int inline_size = le16_to_cpu(di->i_xattr_inline_size);
- int header_off = osb->sb->s_blocksize - inline_size;
- struct ocfs2_xattr_header *xh = (struct ocfs2_xattr_header *)
- (args->old_bh->b_data + header_off);
- struct ocfs2_xattr_header *new_xh = (struct ocfs2_xattr_header *)
- (args->new_bh->b_data + header_off);
+ int inline_size;
+ int header_off;
+ struct ocfs2_xattr_header *xh;
+ struct ocfs2_xattr_header *new_xh;
struct ocfs2_alloc_context *meta_ac = NULL;
struct ocfs2_inode_info *new_oi;
struct ocfs2_dinode *new_di;
@@ -6509,6 +6523,15 @@ static int ocfs2_reflink_xattr_inline(struct ocfs2_xattr_reflink *args)
.vb_access = ocfs2_journal_access_di,
};
+ ret = ocfs2_xattr_ibody_lookup_header(args->old_inode, di, &xh);
+ if (ret)
+ goto out;
+
+ inline_size = le16_to_cpu(di->i_xattr_inline_size);
+ header_off = osb->sb->s_blocksize - inline_size;
+ new_xh = (struct ocfs2_xattr_header *)
+ (args->new_bh->b_data + header_off);
+
ret = ocfs2_reflink_lock_xattr_allocators(osb, xh, args->ref_root_bh,
&credits, &meta_ac);
if (ret) {
diff --git a/fs/proc/base.c b/fs/proc/base.c
index d9acfa89c894b..49344de101582 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -3543,28 +3543,42 @@ out:
struct tgid_iter {
unsigned int tgid;
struct task_struct *task;
+ struct pid_namespace *const pid_ns;
};
-static struct tgid_iter next_tgid(struct pid_namespace *ns, struct tgid_iter iter)
+
+static struct tgid_iter
+make_tgid_iter(unsigned int init_tgid, struct pid_namespace *pid_ns)
{
- struct pid *pid;
+ return (struct tgid_iter){
+ .tgid = init_tgid - 1,
+ .pid_ns = pid_ns,
+ };
+}
+
+static bool next_tgid(struct tgid_iter *it)
+{
+ if (it->task) {
+ put_task_struct(it->task);
+ it->task = NULL;
+ }
- if (iter.task)
- put_task_struct(iter.task);
rcu_read_lock();
-retry:
- iter.task = NULL;
- pid = find_ge_pid(iter.tgid, ns);
- if (pid) {
- iter.tgid = pid_nr_ns(pid, ns);
- iter.task = pid_task(pid, PIDTYPE_TGID);
- if (!iter.task) {
- iter.tgid += 1;
- goto retry;
+ while (1) {
+ it->tgid += 1;
+ const auto pid = find_ge_pid(it->tgid, it->pid_ns);
+ if (pid) {
+ it->tgid = pid_nr_ns(pid, it->pid_ns);
+ it->task = pid_task(pid, PIDTYPE_TGID);
+ if (it->task) {
+ get_task_struct(it->task);
+ rcu_read_unlock();
+ return true;
+ }
+ } else {
+ rcu_read_unlock();
+ return false;
}
- get_task_struct(iter.task);
}
- rcu_read_unlock();
- return iter;
}
#define TGID_OFFSET (FIRST_PROCESS_ENTRY + 2)
@@ -3572,9 +3586,8 @@ retry:
/* for the /proc/ directory itself, after non-process stuff has been done */
int proc_pid_readdir(struct file *file, struct dir_context *ctx)
{
- struct tgid_iter iter;
struct proc_fs_info *fs_info = proc_sb_info(file_inode(file)->i_sb);
- struct pid_namespace *ns = proc_pid_ns(file_inode(file)->i_sb);
+ struct pid_namespace *pid_ns = proc_pid_ns(file_inode(file)->i_sb);
loff_t pos = ctx->pos;
if (pos >= PID_MAX_LIMIT + TGID_OFFSET)
@@ -3590,11 +3603,9 @@ int proc_pid_readdir(struct file *file, struct dir_context *ctx)
return 0;
ctx->pos = pos = pos + 1;
}
- iter.tgid = pos - TGID_OFFSET;
- iter.task = NULL;
- for (iter = next_tgid(ns, iter);
- iter.task;
- iter.tgid += 1, iter = next_tgid(ns, iter)) {
+
+ auto iter = make_tgid_iter(pos - TGID_OFFSET, pid_ns);
+ while (next_tgid(&iter)) {
char name[10 + 1];
unsigned int len;
diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 8bb81e58c9d8c..3063080f3bb2a 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -427,9 +427,13 @@ static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent,
if (xlate_proc_name(name, parent, &fn) != 0)
goto out;
qstr.name = fn;
- qstr.len = strlen(fn);
- if (qstr.len == 0 || qstr.len >= 256) {
- WARN(1, "name len %u\n", qstr.len);
+ qstr.len = strnlen(fn, NAME_MAX + 1);
+ if (qstr.len == 0) {
+ WARN(1, "empty name\n");
+ return NULL;
+ }
+ if (qstr.len > NAME_MAX) {
+ WARN(1, "name too long\n");
return NULL;
}
if (qstr.len == 1 && fn[0] == '.') {