summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOGAWA Hirofumi <hirofumi@mail.parknet.co.jp>2019-09-23 18:32:53 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2019-09-24 18:54:06 -0400
commit07bfa4415ab607e459b69bd86aa7e7602ce10b4f (patch)
tree62a7bc60deca51a1a795c8a22293e658d88efc07
parent619e17cf75dd58905aa67ccd494a6ba5f19d6cc6 (diff)
fat: work around race with userspace's read via blockdev while mounting
If userspace reads the buffer via blockdev while mounting, sb_getblk()+modify can race with buffer read via blockdev. For example, FS userspace bh = sb_getblk() modify bh->b_data read ll_rw_block(bh) fill bh->b_data by on-disk data /* lost modified data by FS */ set_buffer_uptodate(bh) set_buffer_uptodate(bh) Userspace should not use the blockdev while mounting though, the udev seems to be already doing this. Although I think the udev should try to avoid this, workaround the race by small overhead. Link: http://lkml.kernel.org/r/87pnk7l3sw.fsf_-_@mail.parknet.co.jp Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> Reported-by: Jan Stancek <jstancek@redhat.com> Tested-by: Jan Stancek <jstancek@redhat.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--fs/fat/dir.c13
-rw-r--r--fs/fat/fatent.c3
2 files changed, 14 insertions, 2 deletions
diff --git a/fs/fat/dir.c b/fs/fat/dir.c
index 1bda2ab6745b..814ad2c2ba80 100644
--- a/fs/fat/dir.c
+++ b/fs/fat/dir.c
@@ -1100,8 +1100,11 @@ static int fat_zeroed_cluster(struct inode *dir, sector_t blknr, int nr_used,
1100 err = -ENOMEM; 1100 err = -ENOMEM;
1101 goto error; 1101 goto error;
1102 } 1102 }
1103 /* Avoid race with userspace read via bdev */
1104 lock_buffer(bhs[n]);
1103 memset(bhs[n]->b_data, 0, sb->s_blocksize); 1105 memset(bhs[n]->b_data, 0, sb->s_blocksize);
1104 set_buffer_uptodate(bhs[n]); 1106 set_buffer_uptodate(bhs[n]);
1107 unlock_buffer(bhs[n]);
1105 mark_buffer_dirty_inode(bhs[n], dir); 1108 mark_buffer_dirty_inode(bhs[n], dir);
1106 1109
1107 n++; 1110 n++;
@@ -1158,6 +1161,8 @@ int fat_alloc_new_dir(struct inode *dir, struct timespec64 *ts)
1158 fat_time_unix2fat(sbi, ts, &time, &date, &time_cs); 1161 fat_time_unix2fat(sbi, ts, &time, &date, &time_cs);
1159 1162
1160 de = (struct msdos_dir_entry *)bhs[0]->b_data; 1163 de = (struct msdos_dir_entry *)bhs[0]->b_data;
1164 /* Avoid race with userspace read via bdev */
1165 lock_buffer(bhs[0]);
1161 /* filling the new directory slots ("." and ".." entries) */ 1166 /* filling the new directory slots ("." and ".." entries) */
1162 memcpy(de[0].name, MSDOS_DOT, MSDOS_NAME); 1167 memcpy(de[0].name, MSDOS_DOT, MSDOS_NAME);
1163 memcpy(de[1].name, MSDOS_DOTDOT, MSDOS_NAME); 1168 memcpy(de[1].name, MSDOS_DOTDOT, MSDOS_NAME);
@@ -1180,6 +1185,7 @@ int fat_alloc_new_dir(struct inode *dir, struct timespec64 *ts)
1180 de[0].size = de[1].size = 0; 1185 de[0].size = de[1].size = 0;
1181 memset(de + 2, 0, sb->s_blocksize - 2 * sizeof(*de)); 1186 memset(de + 2, 0, sb->s_blocksize - 2 * sizeof(*de));
1182 set_buffer_uptodate(bhs[0]); 1187 set_buffer_uptodate(bhs[0]);
1188 unlock_buffer(bhs[0]);
1183 mark_buffer_dirty_inode(bhs[0], dir); 1189 mark_buffer_dirty_inode(bhs[0], dir);
1184 1190
1185 err = fat_zeroed_cluster(dir, blknr, 1, bhs, MAX_BUF_PER_PAGE); 1191 err = fat_zeroed_cluster(dir, blknr, 1, bhs, MAX_BUF_PER_PAGE);
@@ -1237,11 +1243,14 @@ static int fat_add_new_entries(struct inode *dir, void *slots, int nr_slots,
1237 1243
1238 /* fill the directory entry */ 1244 /* fill the directory entry */
1239 copy = min(size, sb->s_blocksize); 1245 copy = min(size, sb->s_blocksize);
1246 /* Avoid race with userspace read via bdev */
1247 lock_buffer(bhs[n]);
1240 memcpy(bhs[n]->b_data, slots, copy); 1248 memcpy(bhs[n]->b_data, slots, copy);
1241 slots += copy;
1242 size -= copy;
1243 set_buffer_uptodate(bhs[n]); 1249 set_buffer_uptodate(bhs[n]);
1250 unlock_buffer(bhs[n]);
1244 mark_buffer_dirty_inode(bhs[n], dir); 1251 mark_buffer_dirty_inode(bhs[n], dir);
1252 slots += copy;
1253 size -= copy;
1245 if (!size) 1254 if (!size)
1246 break; 1255 break;
1247 n++; 1256 n++;
diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c
index 265983635f2b..3647c65a0f48 100644
--- a/fs/fat/fatent.c
+++ b/fs/fat/fatent.c
@@ -388,8 +388,11 @@ static int fat_mirror_bhs(struct super_block *sb, struct buffer_head **bhs,
388 err = -ENOMEM; 388 err = -ENOMEM;
389 goto error; 389 goto error;
390 } 390 }
391 /* Avoid race with userspace read via bdev */
392 lock_buffer(c_bh);
391 memcpy(c_bh->b_data, bhs[n]->b_data, sb->s_blocksize); 393 memcpy(c_bh->b_data, bhs[n]->b_data, sb->s_blocksize);
392 set_buffer_uptodate(c_bh); 394 set_buffer_uptodate(c_bh);
395 unlock_buffer(c_bh);
393 mark_buffer_dirty_inode(c_bh, sbi->fat_inode); 396 mark_buffer_dirty_inode(c_bh, sbi->fat_inode);
394 if (sb->s_flags & SB_SYNCHRONOUS) 397 if (sb->s_flags & SB_SYNCHRONOUS)
395 err = sync_dirty_buffer(c_bh); 398 err = sync_dirty_buffer(c_bh);