Skip to content

Commit f4e3634

Browse files
Zhihao Chengrichardweinberger
authored andcommitted
ubifs: Fix races between xattr_{set|get} and listxattr operations
UBIFS may occur some problems with concurrent xattr_{set|get} and listxattr operations, such as assertion failure, memory corruption, stale xattr value[1]. Fix it by importing a new rw-lock in @ubifs_inode to serilize write operations on xattr, concurrent read operations are still effective, just like ext4. [1] https://lore.kernel.org/linux-mtd/[email protected] Fixes: 1e51764 ("UBIFS: add new flash file system") Cc: [email protected] # v2.6+ Signed-off-by: Zhihao Cheng <[email protected]> Reviewed-by: Sascha Hauer <[email protected]> Signed-off-by: Richard Weinberger <[email protected]>
1 parent be076fd commit f4e3634

File tree

3 files changed

+36
-11
lines changed

3 files changed

+36
-11
lines changed

fs/ubifs/super.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ static struct inode *ubifs_alloc_inode(struct super_block *sb)
275275
memset((void *)ui + sizeof(struct inode), 0,
276276
sizeof(struct ubifs_inode) - sizeof(struct inode));
277277
mutex_init(&ui->ui_mutex);
278+
init_rwsem(&ui->xattr_sem);
278279
spin_lock_init(&ui->ui_lock);
279280
return &ui->vfs_inode;
280281
};

fs/ubifs/ubifs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ struct ubifs_gced_idx_leb {
356356
* @ui_mutex: serializes inode write-back with the rest of VFS operations,
357357
* serializes "clean <-> dirty" state changes, serializes bulk-read,
358358
* protects @dirty, @bulk_read, @ui_size, and @xattr_size
359+
* @xattr_sem: serilizes write operations (remove|set|create) on xattr
359360
* @ui_lock: protects @synced_i_size
360361
* @synced_i_size: synchronized size of inode, i.e. the value of inode size
361362
* currently stored on the flash; used only for regular file
@@ -409,6 +410,7 @@ struct ubifs_inode {
409410
unsigned int bulk_read:1;
410411
unsigned int compr_type:2;
411412
struct mutex ui_mutex;
413+
struct rw_semaphore xattr_sem;
412414
spinlock_t ui_lock;
413415
loff_t synced_i_size;
414416
loff_t ui_size;

fs/ubifs/xattr.c

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ int ubifs_xattr_set(struct inode *host, const char *name, const void *value,
285285
if (!xent)
286286
return -ENOMEM;
287287

288+
down_write(&ubifs_inode(host)->xattr_sem);
288289
/*
289290
* The extended attribute entries are stored in LNC, so multiple
290291
* look-ups do not involve reading the flash.
@@ -319,6 +320,7 @@ int ubifs_xattr_set(struct inode *host, const char *name, const void *value,
319320
iput(inode);
320321

321322
out_free:
323+
up_write(&ubifs_inode(host)->xattr_sem);
322324
kfree(xent);
323325
return err;
324326
}
@@ -341,18 +343,19 @@ ssize_t ubifs_xattr_get(struct inode *host, const char *name, void *buf,
341343
if (!xent)
342344
return -ENOMEM;
343345

346+
down_read(&ubifs_inode(host)->xattr_sem);
344347
xent_key_init(c, &key, host->i_ino, &nm);
345348
err = ubifs_tnc_lookup_nm(c, &key, xent, &nm);
346349
if (err) {
347350
if (err == -ENOENT)
348351
err = -ENODATA;
349-
goto out_unlock;
352+
goto out_cleanup;
350353
}
351354

352355
inode = iget_xattr(c, le64_to_cpu(xent->inum));
353356
if (IS_ERR(inode)) {
354357
err = PTR_ERR(inode);
355-
goto out_unlock;
358+
goto out_cleanup;
356359
}
357360

358361
ui = ubifs_inode(inode);
@@ -374,7 +377,8 @@ ssize_t ubifs_xattr_get(struct inode *host, const char *name, void *buf,
374377
out_iput:
375378
mutex_unlock(&ui->ui_mutex);
376379
iput(inode);
377-
out_unlock:
380+
out_cleanup:
381+
up_read(&ubifs_inode(host)->xattr_sem);
378382
kfree(xent);
379383
return err;
380384
}
@@ -406,16 +410,21 @@ ssize_t ubifs_listxattr(struct dentry *dentry, char *buffer, size_t size)
406410
dbg_gen("ino %lu ('%pd'), buffer size %zd", host->i_ino,
407411
dentry, size);
408412

413+
down_read(&host_ui->xattr_sem);
409414
len = host_ui->xattr_names + host_ui->xattr_cnt;
410-
if (!buffer)
415+
if (!buffer) {
411416
/*
412417
* We should return the minimum buffer size which will fit a
413418
* null-terminated list of all the extended attribute names.
414419
*/
415-
return len;
420+
err = len;
421+
goto out_err;
422+
}
416423

417-
if (len > size)
418-
return -ERANGE;
424+
if (len > size) {
425+
err = -ERANGE;
426+
goto out_err;
427+
}
419428

420429
lowest_xent_key(c, &key, host->i_ino);
421430
while (1) {
@@ -437,15 +446,20 @@ ssize_t ubifs_listxattr(struct dentry *dentry, char *buffer, size_t size)
437446
pxent = xent;
438447
key_read(c, &xent->key, &key);
439448
}
440-
441449
kfree(pxent);
450+
up_read(&host_ui->xattr_sem);
451+
442452
if (err != -ENOENT) {
443453
ubifs_err(c, "cannot find next direntry, error %d", err);
444454
return err;
445455
}
446456

447457
ubifs_assert(c, written <= size);
448458
return written;
459+
460+
out_err:
461+
up_read(&host_ui->xattr_sem);
462+
return err;
449463
}
450464

451465
static int remove_xattr(struct ubifs_info *c, struct inode *host,
@@ -504,6 +518,7 @@ int ubifs_purge_xattrs(struct inode *host)
504518
ubifs_warn(c, "inode %lu has too many xattrs, doing a non-atomic deletion",
505519
host->i_ino);
506520

521+
down_write(&ubifs_inode(host)->xattr_sem);
507522
lowest_xent_key(c, &key, host->i_ino);
508523
while (1) {
509524
xent = ubifs_tnc_next_ent(c, &key, &nm);
@@ -523,7 +538,7 @@ int ubifs_purge_xattrs(struct inode *host)
523538
ubifs_ro_mode(c, err);
524539
kfree(pxent);
525540
kfree(xent);
526-
return err;
541+
goto out_err;
527542
}
528543

529544
ubifs_assert(c, ubifs_inode(xino)->xattr);
@@ -535,7 +550,7 @@ int ubifs_purge_xattrs(struct inode *host)
535550
kfree(xent);
536551
iput(xino);
537552
ubifs_err(c, "cannot remove xattr, error %d", err);
538-
return err;
553+
goto out_err;
539554
}
540555

541556
iput(xino);
@@ -544,14 +559,19 @@ int ubifs_purge_xattrs(struct inode *host)
544559
pxent = xent;
545560
key_read(c, &xent->key, &key);
546561
}
547-
548562
kfree(pxent);
563+
up_write(&ubifs_inode(host)->xattr_sem);
564+
549565
if (err != -ENOENT) {
550566
ubifs_err(c, "cannot find next direntry, error %d", err);
551567
return err;
552568
}
553569

554570
return 0;
571+
572+
out_err:
573+
up_write(&ubifs_inode(host)->xattr_sem);
574+
return err;
555575
}
556576

557577
/**
@@ -594,6 +614,7 @@ static int ubifs_xattr_remove(struct inode *host, const char *name)
594614
if (!xent)
595615
return -ENOMEM;
596616

617+
down_write(&ubifs_inode(host)->xattr_sem);
597618
xent_key_init(c, &key, host->i_ino, &nm);
598619
err = ubifs_tnc_lookup_nm(c, &key, xent, &nm);
599620
if (err) {
@@ -618,6 +639,7 @@ static int ubifs_xattr_remove(struct inode *host, const char *name)
618639
iput(inode);
619640

620641
out_free:
642+
up_write(&ubifs_inode(host)->xattr_sem);
621643
kfree(xent);
622644
return err;
623645
}

0 commit comments

Comments
 (0)