summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-09-07 12:27:17 +0100
committerMark Brown <broonie@kernel.org>2026-09-07 12:27:17 +0100
commit61b8104799ace86bc6686c853c279a2dd182ad04 (patch)
treea4d87c8525a0f9ceab91338844d4d6295e8cae60
parentacd51194e20325a231de559c36931310c1cc25ef (diff)
parentea51f3f0d3a5df7d96b6afb3bb5a59fa4953d11e (diff)
downloadlinux-next-61b8104799ace86bc6686c853c279a2dd182ad04.tar.gz
linux-next-61b8104799ace86bc6686c853c279a2dd182ad04.zip
Merge branch 'for_next' of https://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs.git
-rw-r--r--fs/ext2/Makefile2
-rw-r--r--fs/ext2/balloc.c4
-rw-r--r--fs/ext2/ext2.h19
-rw-r--r--fs/ext2/inode.c7
-rw-r--r--fs/ext2/super.c33
-rw-r--r--fs/notify/fanotify/fanotify_user.c2
-rw-r--r--fs/ocfs2/quota_local.c4
-rw-r--r--fs/udf/inode.c52
-rw-r--r--fs/udf/misc.c15
-rw-r--r--fs/udf/super.c5
-rw-r--r--mm/shmem_quota.c5
11 files changed, 112 insertions, 36 deletions
diff --git a/fs/ext2/Makefile b/fs/ext2/Makefile
index 8860948ef9ca..33db2e9dc908 100644
--- a/fs/ext2/Makefile
+++ b/fs/ext2/Makefile
@@ -3,6 +3,8 @@
# Makefile for the linux ext2-filesystem routines.
#
+CONTEXT_ANALYSIS := y
+
obj-$(CONFIG_EXT2_FS) += ext2.o
ext2-y := balloc.o dir.o file.o ialloc.o inode.o \
diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c
index adf0f31fbddd..80acc1e19387 100644
--- a/fs/ext2/balloc.c
+++ b/fs/ext2/balloc.c
@@ -334,6 +334,7 @@ search_reserve_window(struct rb_root *root, ext2_fsblk_t goal)
*/
void ext2_rsv_window_add(struct super_block *sb,
struct ext2_reserve_window_node *rsv)
+ __must_hold(&EXT2_SB(sb)->s_rsv_window_lock)
{
struct rb_root *root = &EXT2_SB(sb)->s_rsv_window_root;
struct rb_node *node = &rsv->rsv_node;
@@ -373,6 +374,7 @@ void ext2_rsv_window_add(struct super_block *sb,
*/
static void rsv_window_remove(struct super_block *sb,
struct ext2_reserve_window_node *rsv)
+ __must_hold(&EXT2_SB(sb)->s_rsv_window_lock)
{
rsv->rsv_start = EXT2_RESERVE_WINDOW_NOT_ALLOCATED;
rsv->rsv_end = EXT2_RESERVE_WINDOW_NOT_ALLOCATED;
@@ -414,6 +416,7 @@ static inline int rsv_is_empty(struct ext2_reserve_window *rsv)
* Needs truncate_mutex protection prior to calling this function.
*/
void ext2_init_block_alloc_info(struct inode *inode)
+ __must_hold(&EXT2_I(inode)->truncate_mutex)
{
struct ext2_inode_info *ei = EXT2_I(inode);
struct ext2_block_alloc_info *block_i;
@@ -758,6 +761,7 @@ static int find_next_reservable_window(
struct super_block * sb,
ext2_fsblk_t start_block,
ext2_fsblk_t last_block)
+ __must_hold(&EXT2_SB(sb)->s_rsv_window_lock)
{
struct rb_node *next;
struct ext2_reserve_window_node *rsv, *prev;
diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h
index 5642451bf191..7aeb7cfb0ceb 100644
--- a/fs/ext2/ext2.h
+++ b/fs/ext2/ext2.h
@@ -77,8 +77,8 @@ struct ext2_sb_info {
unsigned long s_gdb_count; /* Number of group descriptor blocks */
unsigned long s_desc_per_block; /* Number of group descriptors per block */
unsigned long s_groups_count; /* Number of groups in the fs */
- unsigned long s_overhead_last; /* Last calculated overhead */
- unsigned long s_blocks_last; /* Last seen block count */
+ unsigned long s_overhead_last __guarded_by(&s_lock); /* Last calculated overhead */
+ unsigned long s_blocks_last __guarded_by(&s_lock); /* Last seen block count */
struct buffer_head * s_sbh; /* Buffer containing the super block */
struct ext2_super_block * s_es; /* Pointer to the super block in the buffer */
struct buffer_head ** s_group_desc;
@@ -86,14 +86,14 @@ struct ext2_sb_info {
unsigned long s_sb_block;
kuid_t s_resuid;
kgid_t s_resgid;
- unsigned short s_mount_state;
+ unsigned short s_mount_state __guarded_by(&s_lock);
unsigned short s_pad;
int s_addr_per_block_bits;
int s_desc_per_block_bits;
int s_inode_size;
int s_first_ino;
spinlock_t s_next_gen_lock;
- u32 s_next_generation;
+ u32 s_next_generation __guarded_by(&s_next_gen_lock);
unsigned long s_dir_count;
u8 *s_debts;
struct percpu_counter s_freeblocks_counter;
@@ -102,7 +102,7 @@ struct ext2_sb_info {
struct blockgroup_lock *s_blockgroup_lock;
/* root of the per fs reservation window tree */
spinlock_t s_rsv_window_lock;
- struct rb_root s_rsv_window_root;
+ struct rb_root s_rsv_window_root __guarded_by(&s_rsv_window_lock);
struct ext2_reserve_window_node s_rsv_window_head;
/*
* s_lock protects against concurrent modifications of s_mount_state,
@@ -710,8 +710,10 @@ extern struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb,
struct buffer_head ** bh);
extern void ext2_discard_reservation (struct inode *);
extern int ext2_should_retry_alloc(struct super_block *sb, int *retries);
-extern void ext2_init_block_alloc_info(struct inode *);
-extern void ext2_rsv_window_add(struct super_block *sb, struct ext2_reserve_window_node *rsv);
+extern void ext2_init_block_alloc_info(struct inode *inode)
+ __must_hold(&EXT2_I(inode)->truncate_mutex);
+extern void ext2_rsv_window_add(struct super_block *sb, struct ext2_reserve_window_node *rsv)
+ __must_hold(&EXT2_SB(sb)->s_rsv_window_lock);
/* dir.c */
int ext2_add_link(struct dentry *, struct inode *);
@@ -761,7 +763,8 @@ extern __printf(3, 4)
void ext2_error(struct super_block *, const char *, const char *, ...);
extern __printf(3, 4)
void ext2_msg(struct super_block *, const char *, const char *, ...);
-extern void ext2_update_dynamic_rev (struct super_block *sb);
+extern void ext2_update_dynamic_rev(struct super_block *sb)
+ __must_hold(&EXT2_SB(sb)->s_lock);
extern void ext2_sync_super(struct super_block *sb, struct ext2_super_block *es,
int wait);
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index a9245f0cda4d..1a1ea1fd485b 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -327,6 +327,7 @@ static ext2_fsblk_t ext2_find_near(struct inode *inode, Indirect *ind)
static inline ext2_fsblk_t ext2_find_goal(struct inode *inode, long block,
Indirect *partial)
+ __must_hold(&EXT2_I(inode)->truncate_mutex)
{
struct ext2_block_alloc_info *block_i;
@@ -397,6 +398,7 @@ ext2_blks_to_allocate(Indirect * branch, int k, unsigned long blks,
static int ext2_alloc_blocks(struct inode *inode,
ext2_fsblk_t goal, int indirect_blks, int blks,
ext2_fsblk_t new_blocks[4], int *err)
+ __must_hold(&EXT2_I(inode)->truncate_mutex)
{
int target, i;
unsigned long count = 0;
@@ -477,6 +479,7 @@ failed_out:
static int ext2_alloc_branch(struct inode *inode,
int indirect_blks, int *blks, ext2_fsblk_t goal,
int *offsets, Indirect *branch)
+ __must_hold(&EXT2_I(inode)->truncate_mutex)
{
int blocksize = inode->i_sb->s_blocksize;
int i, n = 0;
@@ -558,6 +561,7 @@ failed:
*/
static void ext2_splice_branch(struct inode *inode,
long block, Indirect *where, int num, int blks)
+ __must_hold(&EXT2_I(inode)->truncate_mutex)
{
int i;
struct ext2_block_alloc_info *block_i;
@@ -1002,6 +1006,7 @@ static Indirect *ext2_find_shared(struct inode *inode,
int offsets[4],
Indirect chain[4],
__le32 *top)
+ __must_hold(&EXT2_I(inode)->truncate_mutex)
{
Indirect *partial, *p;
int k, err;
@@ -1057,6 +1062,7 @@ no_top:
* appropriately.
*/
static inline void ext2_free_data(struct inode *inode, __le32 *p, __le32 *q)
+ __must_hold(&EXT2_I(inode)->truncate_mutex)
{
ext2_fsblk_t block_to_free = 0, count = 0;
ext2_fsblk_t nr;
@@ -1097,6 +1103,7 @@ static inline void ext2_free_data(struct inode *inode, __le32 *p, __le32 *q)
* appropriately.
*/
static void ext2_free_branches(struct inode *inode, __le32 *p, __le32 *q, int depth)
+ __must_hold(&EXT2_I(inode)->truncate_mutex)
{
struct buffer_head * bh;
ext2_fsblk_t nr;
diff --git a/fs/ext2/super.c b/fs/ext2/super.c
index a40f530872a4..fb217ef2d69f 100644
--- a/fs/ext2/super.c
+++ b/fs/ext2/super.c
@@ -127,6 +127,7 @@ void ext2_msg(struct super_block *sb, const char *prefix,
* This must be called with sbi->s_lock held.
*/
void ext2_update_dynamic_rev(struct super_block *sb)
+ __must_hold(&EXT2_SB(sb)->s_lock)
{
struct ext2_super_block *es = EXT2_SB(sb)->s_es;
@@ -633,6 +634,7 @@ static int ext2_parse_param(struct fs_context *fc, struct fs_parameter *param)
static int ext2_setup_super (struct super_block * sb,
struct ext2_super_block * es,
int read_only)
+ __must_hold(&EXT2_SB(sb)->s_lock)
{
int res = 0;
struct ext2_sb_info *sbi = EXT2_SB(sb);
@@ -894,7 +896,7 @@ static int ext2_fill_super(struct super_block *sb, struct fs_context *fc)
sb->s_fs_info = sbi;
sbi->s_sb_block = sb_block;
- spin_lock_init(&sbi->s_lock);
+ guard(spinlock_init)(&EXT2_SB(sb)->s_lock);
ret = -EINVAL;
/*
@@ -1126,23 +1128,26 @@ static int ext2_fill_super(struct super_block *sb, struct fs_context *fc)
goto failed_mount2;
}
sbi->s_gdb_count = db_count;
- sbi->s_next_generation = get_random_u32();
spin_lock_init(&sbi->s_next_gen_lock);
+ scoped_guard(spinlock, &sbi->s_next_gen_lock)
+ sbi->s_next_generation = get_random_u32();
/* per filesystem reservation list head & lock */
spin_lock_init(&sbi->s_rsv_window_lock);
- sbi->s_rsv_window_root = RB_ROOT;
- /*
- * Add a single, static dummy reservation to the start of the
- * reservation window list --- it gives us a placeholder for
- * append-at-start-of-list which makes the allocation logic
- * _much_ simpler.
- */
- sbi->s_rsv_window_head.rsv_start = EXT2_RESERVE_WINDOW_NOT_ALLOCATED;
- sbi->s_rsv_window_head.rsv_end = EXT2_RESERVE_WINDOW_NOT_ALLOCATED;
- sbi->s_rsv_window_head.rsv_alloc_hit = 0;
- sbi->s_rsv_window_head.rsv_goal_size = 0;
- ext2_rsv_window_add(sb, &sbi->s_rsv_window_head);
+ scoped_guard(spinlock, &EXT2_SB(sb)->s_rsv_window_lock) {
+ sbi->s_rsv_window_root = RB_ROOT;
+ /*
+ * Add a single, static dummy reservation to the start of the
+ * reservation window list --- it gives us a placeholder for
+ * append-at-start-of-list which makes the allocation logic
+ * _much_ simpler.
+ */
+ sbi->s_rsv_window_head.rsv_start = EXT2_RESERVE_WINDOW_NOT_ALLOCATED;
+ sbi->s_rsv_window_head.rsv_end = EXT2_RESERVE_WINDOW_NOT_ALLOCATED;
+ sbi->s_rsv_window_head.rsv_alloc_hit = 0;
+ sbi->s_rsv_window_head.rsv_goal_size = 0;
+ ext2_rsv_window_add(sb, &sbi->s_rsv_window_head);
+ }
err = percpu_counter_init(&sbi->s_freeblocks_counter,
ext2_count_free_blocks(sb), GFP_KERNEL);
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index a32c6634d592..63c9759fc3b0 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -1837,7 +1837,7 @@ static int fanotify_events_supported(struct fsnotify_group *group,
/*
* mount and sb marks are not allowed on kernel internal pseudo fs,
* like pipe_mnt, because that would subscribe to events on all the
- * anonynous pipes in the system.
+ * anonymous pipes in the system.
*
* SB_NOUSER covers all of the internal pseudo fs whose objects are not
* exposed to user's mount namespace, but there are other SB_KERNMOUNT
diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c
index f55810c59b1b..a2b276bcd0a8 100644
--- a/fs/ocfs2/quota_local.c
+++ b/fs/ocfs2/quota_local.c
@@ -171,6 +171,10 @@ static int ocfs2_local_check_quota_file(struct super_block *sb, int type)
struct ocfs2_disk_dqheader *dqhead;
int status, ret = 0;
+ /* OCFS2 quota format is supported only for OCFS2 filesystems */
+ if (sb->s_magic != OCFS2_SUPER_MAGIC)
+ goto out_err;
+
/* First check whether we understand local quota file */
status = ocfs2_read_quota_block(linode, 0, &bh);
if (status) {
diff --git a/fs/udf/inode.c b/fs/udf/inode.c
index e45e546a739a..8babc2fbce9a 100644
--- a/fs/udf/inode.c
+++ b/fs/udf/inode.c
@@ -1337,6 +1337,26 @@ update_time:
}
/*
+ * Verify validity of struct deviceSpec on disk. udf_get_extendedattr() has
+ * already verified the generic header and made sure attribute fits in the
+ * inode so we just have to make sure attribute space is large enough for
+ * deviceSpec struct and required impUse information.
+ */
+static bool udf_device_spec_valid(struct deviceSpec *dsea)
+{
+ u32 attr_length, imp_use_length;
+
+ attr_length = le32_to_cpu(dsea->attrLength);
+ imp_use_length = le32_to_cpu(dsea->impUseLength);
+ if (attr_length < sizeof(struct deviceSpec) ||
+ imp_use_length < sizeof(struct regid) ||
+ imp_use_length > attr_length - sizeof(struct deviceSpec))
+ return false;
+
+ return true;
+}
+
+/*
* Maximum length of linked list formed by ICB hierarchy. The chosen number is
* arbitrary - just that we hopefully don't limit any real use of rewritten
* inode on write-once media but avoid looping for too long on corrupted media.
@@ -1654,13 +1674,19 @@ reread:
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) {
struct deviceSpec *dsea =
(struct deviceSpec *)udf_get_extendedattr(inode, 12, 1);
- if (dsea) {
- init_special_inode(inode, inode->i_mode,
+
+ if (IS_ERR(dsea)) {
+ ret = PTR_ERR(dsea);
+ goto out;
+ }
+ /* Device inodes must have a device spec attribute */
+ if (!dsea || !udf_device_spec_valid(dsea)) {
+ ret = -EFSCORRUPTED;
+ goto out;
+ }
+ init_special_inode(inode, inode->i_mode,
MKDEV(le32_to_cpu(dsea->majorDeviceIdent),
le32_to_cpu(dsea->minorDeviceIdent)));
- /* Developer ID ??? */
- } else
- goto out;
}
ret = 0;
out:
@@ -1757,6 +1783,7 @@ int udf_write_inode(struct inode *inode, struct writeback_control *wbc)
struct udf_sb_info *sbi = UDF_SB(inode->i_sb);
unsigned char blocksize_bits = inode->i_sb->s_blocksize_bits;
struct udf_inode_info *iinfo = UDF_I(inode);
+ int err;
bh = sb_getblk(inode->i_sb,
udf_get_lb_pblock(inode->i_sb, &iinfo->i_location, 0));
@@ -1816,11 +1843,21 @@ int udf_write_inode(struct inode *inode, struct writeback_control *wbc)
struct regid *eid;
struct deviceSpec *dsea =
(struct deviceSpec *)udf_get_extendedattr(inode, 12, 1);
+
+ /* Validity of extended attrs was checked on load */
+ if (WARN_ON_ONCE(IS_ERR(dsea))) {
+ err = PTR_ERR(dsea);
+ goto out_unlock;
+ }
if (!dsea) {
dsea = (struct deviceSpec *)
udf_add_extendedattr(inode,
sizeof(struct deviceSpec) +
sizeof(struct regid), 12, 0x3);
+ if (IS_ERR(dsea)) {
+ err = PTR_ERR(dsea);
+ goto out_unlock;
+ }
dsea->attrType = cpu_to_le32(12);
dsea->attrSubtype = 1;
dsea->attrLength = cpu_to_le32(
@@ -1962,6 +1999,11 @@ finish:
set_inode_metadata_writeback(inode);
return 0;
+
+out_unlock:
+ unlock_buffer(bh);
+ brelse(bh);
+ return err;
}
struct inode *__udf_iget(struct super_block *sb, struct kernel_lb_addr *ino,
diff --git a/fs/udf/misc.c b/fs/udf/misc.c
index 6928e378fbbd..a2084dfbfbd6 100644
--- a/fs/udf/misc.c
+++ b/fs/udf/misc.c
@@ -58,7 +58,7 @@ struct genericFormat *udf_add_extendedattr(struct inode *inode, uint32_t size,
cpu_to_le16(TAG_IDENT_EAHD) ||
le32_to_cpu(eahd->descTag.tagLocation) !=
iinfo->i_location.logicalBlockNum)
- return NULL;
+ return ERR_PTR(-EFSCORRUPTED);
} else {
struct udf_sb_info *sbi = UDF_SB(inode->i_sb);
@@ -122,7 +122,7 @@ struct genericFormat *udf_add_extendedattr(struct inode *inode, uint32_t size,
return (struct genericFormat *)&ea[offset];
}
- return NULL;
+ return ERR_PTR(-ENOSPC);
}
struct genericFormat *udf_get_extendedattr(struct inode *inode, uint32_t type,
@@ -144,7 +144,7 @@ struct genericFormat *udf_get_extendedattr(struct inode *inode, uint32_t type,
cpu_to_le16(TAG_IDENT_EAHD) ||
le32_to_cpu(eahd->descTag.tagLocation) !=
iinfo->i_location.logicalBlockNum)
- return NULL;
+ return ERR_PTR(-EFSCORRUPTED);
if (type < 2048)
offset = sizeof(struct extendedAttrHeaderDesc);
@@ -153,16 +153,17 @@ struct genericFormat *udf_get_extendedattr(struct inode *inode, uint32_t type,
else
offset = le32_to_cpu(eahd->appAttrLocation);
- while (offset + sizeof(*gaf) < iinfo->i_lenEAttr) {
+ while (offset <
+ iinfo->i_lenEAttr - sizeof(struct genericFormat)) {
uint32_t attrLength;
gaf = (struct genericFormat *)&ea[offset];
attrLength = le32_to_cpu(gaf->attrLength);
/* Detect undersized elements and buffer overflows */
- if ((attrLength < sizeof(*gaf)) ||
- (attrLength > (iinfo->i_lenEAttr - offset)))
- break;
+ if (attrLength < sizeof(struct genericFormat) ||
+ attrLength > iinfo->i_lenEAttr - offset)
+ return ERR_PTR(-EFSCORRUPTED);
if (le32_to_cpu(gaf->attrType) == type &&
gaf->attrSubtype == subtype)
diff --git a/fs/udf/super.c b/fs/udf/super.c
index 2ba5973ef4dd..5351755aca3e 100644
--- a/fs/udf/super.c
+++ b/fs/udf/super.c
@@ -1617,7 +1617,10 @@ static bool udf_lvid_valid(struct super_block *sb,
parts = le32_to_cpu(lvid->numOfPartitions);
impuselen = le32_to_cpu(lvid->lengthOfImpUse);
- if (parts >= sb->s_blocksize || impuselen >= sb->s_blocksize ||
+ if (sizeof(struct logicalVolIntegrityDescImpUse) > impuselen ||
+ impuselen >= sb->s_blocksize)
+ return false;
+ if (parts >= sb->s_blocksize ||
sizeof(struct logicalVolIntegrityDesc) + impuselen +
2 * parts * sizeof(u32) > sb->s_blocksize)
return false;
diff --git a/mm/shmem_quota.c b/mm/shmem_quota.c
index d0b92d6da50f..a3fa4bd93e75 100644
--- a/mm/shmem_quota.c
+++ b/mm/shmem_quota.c
@@ -30,6 +30,7 @@
#include <linux/slab.h>
#include <linux/rbtree.h>
#include <linux/shmem_fs.h>
+#include <linux/magic.h>
#include <linux/quotaops.h>
#include <linux/quota.h>
@@ -54,6 +55,10 @@ struct quota_id {
static int shmem_check_quota_file(struct super_block *sb, int type)
{
+ /* Verify enabling happens on tmpfs superblock */
+ if (sb->s_magic != TMPFS_MAGIC)
+ return 0;
+
/* There is no real quota file, nothing to do */
return 1;
}