Skip to content

Commit fbeafe7

Browse files
dubeykoidryomov
authored andcommitted
ceph: fix potential race condition on operations with CEPH_I_ODIRECT flag
The Coverity Scan service has detected potential race conditions in ceph_block_o_direct(), ceph_start_io_read(), ceph_block_buffered(), and ceph_start_io_direct() [1 - 4]. The CID 1590942, 1590665, 1589664, 1590377 contain explanation: "The value of the shared data will be determined by the interleaving of thread execution. Thread shared data is accessed without holding an appropriate lock, possibly causing a race condition (CWE-366)". This patch reworks the pattern of accessing/modification of CEPH_I_ODIRECT flag by means of adding smp_mb__before_atomic() before reading the status of CEPH_I_ODIRECT flag and smp_mb__after_atomic() after clearing set/clear this flag. Also, it was reworked the pattern of using of ci->i_ceph_lock in ceph_block_o_direct(), ceph_start_io_read(), ceph_block_buffered(), and ceph_start_io_direct() methods. [1] https://scan5.scan.coverity.com/#/project-view/64304/10063?selectedIssue=1590942 [2] https://scan5.scan.coverity.com/#/project-view/64304/10063?selectedIssue=1590665 [3] https://scan5.scan.coverity.com/#/project-view/64304/10063?selectedIssue=1589664 [4] https://scan5.scan.coverity.com/#/project-view/64304/10063?selectedIssue=1590377 Signed-off-by: Viacheslav Dubeyko <[email protected]> Reviewed-by: Alex Markuze <[email protected]> Signed-off-by: Ilya Dryomov <[email protected]>
1 parent 53db6f2 commit fbeafe7

File tree

2 files changed

+44
-12
lines changed

2 files changed

+44
-12
lines changed

fs/ceph/io.c

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,23 @@
2121
/* Call with exclusively locked inode->i_rwsem */
2222
static void ceph_block_o_direct(struct ceph_inode_info *ci, struct inode *inode)
2323
{
24+
bool is_odirect;
25+
2426
lockdep_assert_held_write(&inode->i_rwsem);
2527

26-
if (READ_ONCE(ci->i_ceph_flags) & CEPH_I_ODIRECT) {
27-
spin_lock(&ci->i_ceph_lock);
28-
ci->i_ceph_flags &= ~CEPH_I_ODIRECT;
29-
spin_unlock(&ci->i_ceph_lock);
30-
inode_dio_wait(inode);
28+
spin_lock(&ci->i_ceph_lock);
29+
/* ensure that bit state is consistent */
30+
smp_mb__before_atomic();
31+
is_odirect = READ_ONCE(ci->i_ceph_flags) & CEPH_I_ODIRECT;
32+
if (is_odirect) {
33+
clear_bit(CEPH_I_ODIRECT_BIT, &ci->i_ceph_flags);
34+
/* ensure modified bit is visible */
35+
smp_mb__after_atomic();
3136
}
37+
spin_unlock(&ci->i_ceph_lock);
38+
39+
if (is_odirect)
40+
inode_dio_wait(inode);
3241
}
3342

3443
/**
@@ -50,14 +59,20 @@ static void ceph_block_o_direct(struct ceph_inode_info *ci, struct inode *inode)
5059
int ceph_start_io_read(struct inode *inode)
5160
{
5261
struct ceph_inode_info *ci = ceph_inode(inode);
62+
bool is_odirect;
5363
int err;
5464

5565
/* Be an optimist! */
5666
err = down_read_killable(&inode->i_rwsem);
5767
if (err)
5868
return err;
5969

60-
if (!(READ_ONCE(ci->i_ceph_flags) & CEPH_I_ODIRECT))
70+
spin_lock(&ci->i_ceph_lock);
71+
/* ensure that bit state is consistent */
72+
smp_mb__before_atomic();
73+
is_odirect = READ_ONCE(ci->i_ceph_flags) & CEPH_I_ODIRECT;
74+
spin_unlock(&ci->i_ceph_lock);
75+
if (!is_odirect)
6176
return 0;
6277
up_read(&inode->i_rwsem);
6378

@@ -116,12 +131,22 @@ ceph_end_io_write(struct inode *inode)
116131
/* Call with exclusively locked inode->i_rwsem */
117132
static void ceph_block_buffered(struct ceph_inode_info *ci, struct inode *inode)
118133
{
134+
bool is_odirect;
135+
119136
lockdep_assert_held_write(&inode->i_rwsem);
120137

121-
if (!(READ_ONCE(ci->i_ceph_flags) & CEPH_I_ODIRECT)) {
122-
spin_lock(&ci->i_ceph_lock);
123-
ci->i_ceph_flags |= CEPH_I_ODIRECT;
124-
spin_unlock(&ci->i_ceph_lock);
138+
spin_lock(&ci->i_ceph_lock);
139+
/* ensure that bit state is consistent */
140+
smp_mb__before_atomic();
141+
is_odirect = READ_ONCE(ci->i_ceph_flags) & CEPH_I_ODIRECT;
142+
if (!is_odirect) {
143+
set_bit(CEPH_I_ODIRECT_BIT, &ci->i_ceph_flags);
144+
/* ensure modified bit is visible */
145+
smp_mb__after_atomic();
146+
}
147+
spin_unlock(&ci->i_ceph_lock);
148+
149+
if (!is_odirect) {
125150
/* FIXME: unmap_mapping_range? */
126151
filemap_write_and_wait(inode->i_mapping);
127152
}
@@ -146,14 +171,20 @@ static void ceph_block_buffered(struct ceph_inode_info *ci, struct inode *inode)
146171
int ceph_start_io_direct(struct inode *inode)
147172
{
148173
struct ceph_inode_info *ci = ceph_inode(inode);
174+
bool is_odirect;
149175
int err;
150176

151177
/* Be an optimist! */
152178
err = down_read_killable(&inode->i_rwsem);
153179
if (err)
154180
return err;
155181

156-
if (READ_ONCE(ci->i_ceph_flags) & CEPH_I_ODIRECT)
182+
spin_lock(&ci->i_ceph_lock);
183+
/* ensure that bit state is consistent */
184+
smp_mb__before_atomic();
185+
is_odirect = READ_ONCE(ci->i_ceph_flags) & CEPH_I_ODIRECT;
186+
spin_unlock(&ci->i_ceph_lock);
187+
if (is_odirect)
157188
return 0;
158189
up_read(&inode->i_rwsem);
159190

fs/ceph/super.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -638,7 +638,8 @@ static inline struct inode *ceph_find_inode(struct super_block *sb,
638638
#define CEPH_I_FLUSH_SNAPS (1 << 8) /* need flush snapss */
639639
#define CEPH_I_ERROR_WRITE (1 << 9) /* have seen write errors */
640640
#define CEPH_I_ERROR_FILELOCK (1 << 10) /* have seen file lock errors */
641-
#define CEPH_I_ODIRECT (1 << 11) /* inode in direct I/O mode */
641+
#define CEPH_I_ODIRECT_BIT (11) /* inode in direct I/O mode */
642+
#define CEPH_I_ODIRECT (1 << CEPH_I_ODIRECT_BIT)
642643
#define CEPH_ASYNC_CREATE_BIT (12) /* async create in flight for this */
643644
#define CEPH_I_ASYNC_CREATE (1 << CEPH_ASYNC_CREATE_BIT)
644645
#define CEPH_I_SHUTDOWN (1 << 13) /* inode is no longer usable */

0 commit comments

Comments
 (0)