Skip to content

Commit 00a1a05

Browse files
tytsotorvalds
authored andcommitted
ext4: atomically set inode->i_flags in ext4_set_inode_flags()
Use cmpxchg() to atomically set i_flags instead of clearing out the S_IMMUTABLE, S_APPEND, etc. flags and then setting them from the EXT4_IMMUTABLE_FL, EXT4_APPEND_FL flags, since this opens up a race where an immutable file has the immutable flag cleared for a brief window of time. Reported-by: John Sullivan <jsrhbz@kanargh.force9.co.uk> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu> Cc: stable@kernel.org Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1 parent 981e893 commit 00a1a05

File tree

2 files changed

+24
-6
lines changed

2 files changed

+24
-6
lines changed

fs/ext4/inode.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include <linux/slab.h>
3939
#include <linux/ratelimit.h>
4040
#include <linux/aio.h>
41+
#include <linux/bitops.h>
4142

4243
#include "ext4_jbd2.h"
4344
#include "xattr.h"
@@ -3921,18 +3922,20 @@ int ext4_get_inode_loc(struct inode *inode, struct ext4_iloc *iloc)
39213922
void ext4_set_inode_flags(struct inode *inode)
39223923
{
39233924
unsigned int flags = EXT4_I(inode)->i_flags;
3925+
unsigned int new_fl = 0;
39243926

3925-
inode->i_flags &= ~(S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC);
39263927
if (flags & EXT4_SYNC_FL)
3927-
inode->i_flags |= S_SYNC;
3928+
new_fl |= S_SYNC;
39283929
if (flags & EXT4_APPEND_FL)
3929-
inode->i_flags |= S_APPEND;
3930+
new_fl |= S_APPEND;
39303931
if (flags & EXT4_IMMUTABLE_FL)
3931-
inode->i_flags |= S_IMMUTABLE;
3932+
new_fl |= S_IMMUTABLE;
39323933
if (flags & EXT4_NOATIME_FL)
3933-
inode->i_flags |= S_NOATIME;
3934+
new_fl |= S_NOATIME;
39343935
if (flags & EXT4_DIRSYNC_FL)
3935-
inode->i_flags |= S_DIRSYNC;
3936+
new_fl |= S_DIRSYNC;
3937+
set_mask_bits(&inode->i_flags,
3938+
S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC, new_fl);
39363939
}
39373940

39383941
/* Propagate flags from i_flags to EXT4_I(inode)->i_flags */

include/linux/bitops.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,21 @@ static inline unsigned long __ffs64(u64 word)
196196

197197
#ifdef __KERNEL__
198198

199+
#ifndef set_mask_bits
200+
#define set_mask_bits(ptr, _mask, _bits) \
201+
({ \
202+
const typeof(*ptr) mask = (_mask), bits = (_bits); \
203+
typeof(*ptr) old, new; \
204+
\
205+
do { \
206+
old = ACCESS_ONCE(*ptr); \
207+
new = (old & ~mask) | bits; \
208+
} while (cmpxchg(ptr, old, new) != old); \
209+
\
210+
new; \
211+
})
212+
#endif
213+
199214
#ifndef find_last_bit
200215
/**
201216
* find_last_bit - find the last set bit in a memory region

0 commit comments

Comments
 (0)