diff options
author | Tejun Heo <tj@kernel.org> | 2018-03-14 15:10:17 -0400 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2018-03-14 15:10:17 -0400 |
commit | d0264c01e7587001a8c4608a5d1818dba9a4c11a (patch) | |
tree | fe5161b426a3c3071a9b3b5ee590af2ddd03e0d1 | |
parent | a6d7cff472eea87d96899a20fa718d2bab7109f3 (diff) |
fs/aio: Use RCU accessors for kioctx_table->table[]
While converting ioctx index from a list to a table, db446a08c23d
("aio: convert the ioctx list to table lookup v3") missed tagging
kioctx_table->table[] as an array of RCU pointers and using the
appropriate RCU accessors. This introduces a small window in the
lookup path where init and access may race.
Mark kioctx_table->table[] with __rcu and use the approriate RCU
accessors when using the field.
Signed-off-by: Tejun Heo <tj@kernel.org>
Reported-by: Jann Horn <jannh@google.com>
Fixes: db446a08c23d ("aio: convert the ioctx list to table lookup v3")
Cc: Benjamin LaHaise <bcrl@kvack.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: stable@vger.kernel.org # v3.12+
-rw-r--r-- | fs/aio.c | 21 |
1 files changed, 11 insertions, 10 deletions
@@ -68,9 +68,9 @@ struct aio_ring { | |||
68 | #define AIO_RING_PAGES 8 | 68 | #define AIO_RING_PAGES 8 |
69 | 69 | ||
70 | struct kioctx_table { | 70 | struct kioctx_table { |
71 | struct rcu_head rcu; | 71 | struct rcu_head rcu; |
72 | unsigned nr; | 72 | unsigned nr; |
73 | struct kioctx *table[]; | 73 | struct kioctx __rcu *table[]; |
74 | }; | 74 | }; |
75 | 75 | ||
76 | struct kioctx_cpu { | 76 | struct kioctx_cpu { |
@@ -330,7 +330,7 @@ static int aio_ring_mremap(struct vm_area_struct *vma) | |||
330 | for (i = 0; i < table->nr; i++) { | 330 | for (i = 0; i < table->nr; i++) { |
331 | struct kioctx *ctx; | 331 | struct kioctx *ctx; |
332 | 332 | ||
333 | ctx = table->table[i]; | 333 | ctx = rcu_dereference(table->table[i]); |
334 | if (ctx && ctx->aio_ring_file == file) { | 334 | if (ctx && ctx->aio_ring_file == file) { |
335 | if (!atomic_read(&ctx->dead)) { | 335 | if (!atomic_read(&ctx->dead)) { |
336 | ctx->user_id = ctx->mmap_base = vma->vm_start; | 336 | ctx->user_id = ctx->mmap_base = vma->vm_start; |
@@ -666,9 +666,9 @@ static int ioctx_add_table(struct kioctx *ctx, struct mm_struct *mm) | |||
666 | while (1) { | 666 | while (1) { |
667 | if (table) | 667 | if (table) |
668 | for (i = 0; i < table->nr; i++) | 668 | for (i = 0; i < table->nr; i++) |
669 | if (!table->table[i]) { | 669 | if (!rcu_access_pointer(table->table[i])) { |
670 | ctx->id = i; | 670 | ctx->id = i; |
671 | table->table[i] = ctx; | 671 | rcu_assign_pointer(table->table[i], ctx); |
672 | spin_unlock(&mm->ioctx_lock); | 672 | spin_unlock(&mm->ioctx_lock); |
673 | 673 | ||
674 | /* While kioctx setup is in progress, | 674 | /* While kioctx setup is in progress, |
@@ -849,8 +849,8 @@ static int kill_ioctx(struct mm_struct *mm, struct kioctx *ctx, | |||
849 | } | 849 | } |
850 | 850 | ||
851 | table = rcu_dereference_raw(mm->ioctx_table); | 851 | table = rcu_dereference_raw(mm->ioctx_table); |
852 | WARN_ON(ctx != table->table[ctx->id]); | 852 | WARN_ON(ctx != rcu_access_pointer(table->table[ctx->id])); |
853 | table->table[ctx->id] = NULL; | 853 | RCU_INIT_POINTER(table->table[ctx->id], NULL); |
854 | spin_unlock(&mm->ioctx_lock); | 854 | spin_unlock(&mm->ioctx_lock); |
855 | 855 | ||
856 | /* free_ioctx_reqs() will do the necessary RCU synchronization */ | 856 | /* free_ioctx_reqs() will do the necessary RCU synchronization */ |
@@ -895,7 +895,8 @@ void exit_aio(struct mm_struct *mm) | |||
895 | 895 | ||
896 | skipped = 0; | 896 | skipped = 0; |
897 | for (i = 0; i < table->nr; ++i) { | 897 | for (i = 0; i < table->nr; ++i) { |
898 | struct kioctx *ctx = table->table[i]; | 898 | struct kioctx *ctx = |
899 | rcu_dereference_protected(table->table[i], true); | ||
899 | 900 | ||
900 | if (!ctx) { | 901 | if (!ctx) { |
901 | skipped++; | 902 | skipped++; |
@@ -1084,7 +1085,7 @@ static struct kioctx *lookup_ioctx(unsigned long ctx_id) | |||
1084 | if (!table || id >= table->nr) | 1085 | if (!table || id >= table->nr) |
1085 | goto out; | 1086 | goto out; |
1086 | 1087 | ||
1087 | ctx = table->table[id]; | 1088 | ctx = rcu_dereference(table->table[id]); |
1088 | if (ctx && ctx->user_id == ctx_id) { | 1089 | if (ctx && ctx->user_id == ctx_id) { |
1089 | percpu_ref_get(&ctx->users); | 1090 | percpu_ref_get(&ctx->users); |
1090 | ret = ctx; | 1091 | ret = ctx; |