History

UBIFS: prepare to fix a horrid bug

Al Viro pointed me to the fact that '->readdir()' and '->llseek()' have no mutual exclusion, which means the 'ubifs_dir_llseek()' can be run while we are in the middle of 'ubifs_readdir()'. First of all, this means that 'file->private_data' can be freed while 'ubifs_readdir()' uses it. But this particular patch does not fix the problem. This patch is only a preparation, and the fix will follow next. In this patch we make 'ubifs_readdir()' stop using 'file->f_pos' directly, because 'file->f_pos' can be changed by '->llseek()' at any point. This may lead 'ubifs_readdir()' to returning inconsistent data: directory entry names may correspond to incorrect file positions. So here we introduce a local variable 'pos', read 'file->f_pose' once at very the beginning, and then stick to 'pos'. The result of this is that when 'ubifs_dir_llseek()' changes 'file->f_pos' while we are in the middle of 'ubifs_readdir()', the latter "wins".

Artem Bityutskiy committed 13 years ago

Commit 33f1a631 parent 945fb131 file · +12 −12Browse files

Unified · Split

fs/ubifs/dir.c+12−12
@@ -349,15 +349,16 @@ static unsigned int vfs_dent_type(uint8_t type) · 348 unmodified lines
349349 static int ubifs_readdir(struct file *file, void *dirent, filldir_t filldir)
350350 {
351351 int err, over = 0;
352+ loff_t pos = file->f_pos;
352353 struct qstr nm;
353354 union ubifs_key key;
354355 struct ubifs_dent_node *dent;
355356 struct inode *dir = file_inode(file);
356357 struct ubifs_info *c = dir->i_sb->s_fs_info;
357358
358− dbg_gen("dir ino %lu, f_pos %#llx", dir->i_ino, file->f_pos);
359+ dbg_gen("dir ino %lu, f_pos %#llx", dir->i_ino, pos);
359360
360− if (file->f_pos > UBIFS_S_KEY_HASH_MASK || file->f_pos == 2)
361+ if (pos > UBIFS_S_KEY_HASH_MASK || pos == 2)
361362 /*
362363 * The directory was seek'ed to a senseless position or there
363364 * are no more entries.
@@ -365,15 +366,15 @@ static int ubifs_readdir(struct file *file, void *dirent, filldir_t filldir) · 1 unmodified line
365366 return 0;
366367
367368 /* File positions 0 and 1 correspond to "." and ".." */
368− if (file->f_pos == 0) {
369+ if (pos == 0) {
369370 ubifs_assert(!file->private_data);
370371 over = filldir(dirent, ".", 1, 0, dir->i_ino, DT_DIR);
371372 if (over)
372373 return 0;
373− file->f_pos = 1;
374+ file->f_pos = pos = 1;
374375 }
375376
376− if (file->f_pos == 1) {
377+ if (pos == 1) {
377378 ubifs_assert(!file->private_data);
378379 over = filldir(dirent, "..", 2, 1,
379380 parent_ino(file->f_path.dentry), DT_DIR);
@@ -389,7 +390,7 @@ static int ubifs_readdir(struct file *file, void *dirent, filldir_t filldir) · 9 unmodified lines
389390 goto out;
390391 }
391392
392− file->f_pos = key_hash_flash(c, &dent->key);
393+ file->f_pos = pos = key_hash_flash(c, &dent->key);
393394 file->private_data = dent;
394395 }
395396
@@ -397,17 +398,16 @@ static int ubifs_readdir(struct file *file, void *dirent, filldir_t filldir) · 1 unmodified line
397398 if (!dent) {
398399 /*
399400 * The directory was seek'ed to and is now readdir'ed.
400− * Find the entry corresponding to @file->f_pos or the
401− * closest one.
401+ * Find the entry corresponding to @pos or the closest one.
402402 */
403− dent_key_init_hash(c, &key, dir->i_ino, file->f_pos);
403+ dent_key_init_hash(c, &key, dir->i_ino, pos);
404404 nm.name = NULL;
405405 dent = ubifs_tnc_next_ent(c, &key, &nm);
406406 if (IS_ERR(dent)) {
407407 err = PTR_ERR(dent);
408408 goto out;
409409 }
410− file->f_pos = key_hash_flash(c, &dent->key);
410+ file->f_pos = pos = key_hash_flash(c, &dent->key);
411411 file->private_data = dent;
412412 }
413413
@@ -419,7 +419,7 @@ static int ubifs_readdir(struct file *file, void *dirent, filldir_t filldir) · 5 unmodified lines
419419 ubifs_inode(dir)->creat_sqnum);
420420
421421 nm.len = le16_to_cpu(dent->nlen);
422− over = filldir(dirent, dent->name, nm.len, file->f_pos,
422+ over = filldir(dirent, dent->name, nm.len, pos,
423423 le64_to_cpu(dent->inum),
424424 vfs_dent_type(dent->type));
425425 if (over)
@@ -435,7 +435,7 @@ static int ubifs_readdir(struct file *file, void *dirent, filldir_t filldir) · 9 unmodified lines
435435 }
436436
437437 kfree(file->private_data);
438− file->f_pos = key_hash_flash(c, &dent->key);
438+ file->f_pos = pos = key_hash_flash(c, &dent->key);
439439 file->private_data = dent;
440440 cond_resched();
441441 }