From: Greg Kroah-Hartman Date: Mon, 19 Mar 2018 10:02:17 +0000 (+0100) Subject: 3.18-stable patches X-Git-Tag: v4.15.12~27 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d23e17f1249a9cd355b606b46d94bbed6eb5a365;p=thirdparty%2Fkernel%2Fstable-queue.git 3.18-stable patches added patches: alsa-pcm-fix-uaf-in-snd_pcm_oss_get_formats.patch alsa-seq-clear-client-entry-before-deleting-else-at-closing.patch alsa-seq-fix-possible-uaf-in-snd_seq_check_queue.patch fs-aio-add-explicit-rcu-grace-period-when-freeing-kioctx.patch fs-aio-use-rcu-accessors-for-kioctx_table-table.patch lock_parent-needs-to-recheck-if-dentry-got-__dentry_kill-ed-under-it.patch --- diff --git a/queue-3.18/alsa-pcm-fix-uaf-in-snd_pcm_oss_get_formats.patch b/queue-3.18/alsa-pcm-fix-uaf-in-snd_pcm_oss_get_formats.patch new file mode 100644 index 00000000000..25007232bed --- /dev/null +++ b/queue-3.18/alsa-pcm-fix-uaf-in-snd_pcm_oss_get_formats.patch @@ -0,0 +1,51 @@ +From 01c0b4265cc16bc1f43f475c5944c55c10d5768f Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Sat, 10 Mar 2018 23:04:23 +0100 +Subject: ALSA: pcm: Fix UAF in snd_pcm_oss_get_formats() + +From: Takashi Iwai + +commit 01c0b4265cc16bc1f43f475c5944c55c10d5768f upstream. + +snd_pcm_oss_get_formats() has an obvious use-after-free around +snd_mask_test() calls, as spotted by syzbot. The passed format_mask +argument is a pointer to the hw_params object that is freed before the +loop. What a surprise that it has been present since the original +code of decades ago... + +Reported-by: syzbot+4090700a4f13fccaf648@syzkaller.appspotmail.com +Cc: +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman + +--- + sound/core/oss/pcm_oss.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +--- a/sound/core/oss/pcm_oss.c ++++ b/sound/core/oss/pcm_oss.c +@@ -1815,10 +1815,9 @@ static int snd_pcm_oss_get_formats(struc + return -ENOMEM; + _snd_pcm_hw_params_any(params); + err = snd_pcm_hw_refine(substream, params); +- format_mask = *hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); +- kfree(params); + if (err < 0) +- return err; ++ goto error; ++ format_mask = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT); + for (fmt = 0; fmt < 32; ++fmt) { + if (snd_mask_test(&format_mask, fmt)) { + int f = snd_pcm_oss_format_to(fmt); +@@ -1826,7 +1825,10 @@ static int snd_pcm_oss_get_formats(struc + formats |= f; + } + } +- return formats; ++ ++ error: ++ kfree(params); ++ return err < 0 ? err : formats; + } + + static int snd_pcm_oss_set_format(struct snd_pcm_oss_file *pcm_oss_file, int format) diff --git a/queue-3.18/alsa-seq-clear-client-entry-before-deleting-else-at-closing.patch b/queue-3.18/alsa-seq-clear-client-entry-before-deleting-else-at-closing.patch new file mode 100644 index 00000000000..8db06394c7e --- /dev/null +++ b/queue-3.18/alsa-seq-clear-client-entry-before-deleting-else-at-closing.patch @@ -0,0 +1,50 @@ +From a2ff19f7b70118ced291a28d5313469914de451b Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Fri, 9 Mar 2018 22:23:31 +0100 +Subject: ALSA: seq: Clear client entry before deleting else at closing + +From: Takashi Iwai + +commit a2ff19f7b70118ced291a28d5313469914de451b upstream. + +When releasing a client, we need to clear the clienttab[] entry at +first, then call snd_seq_queue_client_leave(). Otherwise, the +in-flight cell in the queue might be picked up by the timer interrupt +via snd_seq_check_queue() before calling snd_seq_queue_client_leave(), +and it's delivered to another queue while the client is clearing +queues. This may eventually result in an uncleared cell remaining in +a queue, and the later snd_seq_pool_delete() may need to wait for a +long time until the event gets really processed. + +By moving the clienttab[] clearance at the beginning of release, any +event delivery of a cell belonging to this client will fail at a later +point, since snd_seq_client_ptr() returns NULL. Thus the cell that +was picked up by the timer interrupt will be returned immediately +without further delivery, and the long stall of snd_seq_delete_pool() +can be avoided, too. + +Cc: +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman + +--- + sound/core/seq/seq_clientmgr.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/sound/core/seq/seq_clientmgr.c ++++ b/sound/core/seq/seq_clientmgr.c +@@ -270,12 +270,12 @@ static int seq_free_client1(struct snd_s + + if (!client) + return 0; +- snd_seq_delete_all_ports(client); +- snd_seq_queue_client_leave(client->number); + spin_lock_irqsave(&clients_lock, flags); + clienttablock[client->number] = 1; + clienttab[client->number] = NULL; + spin_unlock_irqrestore(&clients_lock, flags); ++ snd_seq_delete_all_ports(client); ++ snd_seq_queue_client_leave(client->number); + snd_use_lock_sync(&client->use_lock); + snd_seq_queue_client_termination(client->number); + if (client->pool) diff --git a/queue-3.18/alsa-seq-fix-possible-uaf-in-snd_seq_check_queue.patch b/queue-3.18/alsa-seq-fix-possible-uaf-in-snd_seq_check_queue.patch new file mode 100644 index 00000000000..742edf52684 --- /dev/null +++ b/queue-3.18/alsa-seq-fix-possible-uaf-in-snd_seq_check_queue.patch @@ -0,0 +1,170 @@ +From d0f833065221cbfcbadf19fd4102bcfa9330006a Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Fri, 9 Mar 2018 21:58:28 +0100 +Subject: ALSA: seq: Fix possible UAF in snd_seq_check_queue() + +From: Takashi Iwai + +commit d0f833065221cbfcbadf19fd4102bcfa9330006a upstream. + +Although we've covered the races between concurrent write() and +ioctl() in the previous patch series, there is still a possible UAF in +the following scenario: + +A: user client closed B: timer irq + -> snd_seq_release() -> snd_seq_timer_interrupt() + -> snd_seq_free_client() -> snd_seq_check_queue() + -> cell = snd_seq_prioq_cell_peek() + -> snd_seq_prioq_leave() + .... removing all cells + -> snd_seq_pool_done() + .... vfree() + -> snd_seq_compare_tick_time(cell) + ... Oops + +So the problem is that a cell is peeked and accessed without any +protection until it's retrieved from the queue again via +snd_seq_prioq_cell_out(). + +This patch tries to address it, also cleans up the code by a slight +refactoring. snd_seq_prioq_cell_out() now receives an extra pointer +argument. When it's non-NULL, the function checks the event timestamp +with the given pointer. The caller needs to pass the right reference +either to snd_seq_tick or snd_seq_realtime depending on the event +timestamp type. + +A good news is that the above change allows us to remove the +snd_seq_prioq_cell_peek(), too, thus the patch actually reduces the +code size. + +Reviewed-by: Nicolai Stange +Cc: +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman + +--- + sound/core/seq/seq_prioq.c | 28 ++++++++++++++-------------- + sound/core/seq/seq_prioq.h | 6 ++---- + sound/core/seq/seq_queue.c | 28 +++++++++------------------- + 3 files changed, 25 insertions(+), 37 deletions(-) + +--- a/sound/core/seq/seq_prioq.c ++++ b/sound/core/seq/seq_prioq.c +@@ -87,7 +87,7 @@ void snd_seq_prioq_delete(struct snd_seq + if (f->cells > 0) { + /* drain prioQ */ + while (f->cells > 0) +- snd_seq_cell_free(snd_seq_prioq_cell_out(f)); ++ snd_seq_cell_free(snd_seq_prioq_cell_out(f, NULL)); + } + + kfree(f); +@@ -214,8 +214,18 @@ int snd_seq_prioq_cell_in(struct snd_seq + return 0; + } + ++/* return 1 if the current time >= event timestamp */ ++static int event_is_ready(struct snd_seq_event *ev, void *current_time) ++{ ++ if ((ev->flags & SNDRV_SEQ_TIME_STAMP_MASK) == SNDRV_SEQ_TIME_STAMP_TICK) ++ return snd_seq_compare_tick_time(current_time, &ev->time.tick); ++ else ++ return snd_seq_compare_real_time(current_time, &ev->time.time); ++} ++ + /* dequeue cell from prioq */ +-struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f) ++struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f, ++ void *current_time) + { + struct snd_seq_event_cell *cell; + unsigned long flags; +@@ -227,6 +237,8 @@ struct snd_seq_event_cell *snd_seq_prioq + spin_lock_irqsave(&f->lock, flags); + + cell = f->head; ++ if (cell && current_time && !event_is_ready(&cell->event, current_time)) ++ cell = NULL; + if (cell) { + f->head = cell->next; + +@@ -252,18 +264,6 @@ int snd_seq_prioq_avail(struct snd_seq_p + return f->cells; + } + +- +-/* peek at cell at the head of the prioq */ +-struct snd_seq_event_cell *snd_seq_prioq_cell_peek(struct snd_seq_prioq * f) +-{ +- if (f == NULL) { +- pr_debug("ALSA: seq: snd_seq_prioq_cell_in() called with NULL prioq\n"); +- return NULL; +- } +- return f->head; +-} +- +- + static inline int prioq_match(struct snd_seq_event_cell *cell, + int client, int timestamp) + { +--- a/sound/core/seq/seq_prioq.h ++++ b/sound/core/seq/seq_prioq.h +@@ -44,14 +44,12 @@ void snd_seq_prioq_delete(struct snd_seq + int snd_seq_prioq_cell_in(struct snd_seq_prioq *f, struct snd_seq_event_cell *cell); + + /* dequeue cell from prioq */ +-struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f); ++struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f, ++ void *current_time); + + /* return number of events available in prioq */ + int snd_seq_prioq_avail(struct snd_seq_prioq *f); + +-/* peek at cell at the head of the prioq */ +-struct snd_seq_event_cell *snd_seq_prioq_cell_peek(struct snd_seq_prioq *f); +- + /* client left queue */ + void snd_seq_prioq_leave(struct snd_seq_prioq *f, int client, int timestamp); + +--- a/sound/core/seq/seq_queue.c ++++ b/sound/core/seq/seq_queue.c +@@ -271,30 +271,20 @@ void snd_seq_check_queue(struct snd_seq_ + + __again: + /* Process tick queue... */ +- while ((cell = snd_seq_prioq_cell_peek(q->tickq)) != NULL) { +- if (snd_seq_compare_tick_time(&q->timer->tick.cur_tick, +- &cell->event.time.tick)) { +- cell = snd_seq_prioq_cell_out(q->tickq); +- if (cell) +- snd_seq_dispatch_event(cell, atomic, hop); +- } else { +- /* event remains in the queue */ ++ for (;;) { ++ cell = snd_seq_prioq_cell_out(q->tickq, ++ &q->timer->tick.cur_tick); ++ if (!cell) + break; +- } ++ snd_seq_dispatch_event(cell, atomic, hop); + } + +- + /* Process time queue... */ +- while ((cell = snd_seq_prioq_cell_peek(q->timeq)) != NULL) { +- if (snd_seq_compare_real_time(&q->timer->cur_time, +- &cell->event.time.time)) { +- cell = snd_seq_prioq_cell_out(q->timeq); +- if (cell) +- snd_seq_dispatch_event(cell, atomic, hop); +- } else { +- /* event remains in the queue */ ++ for (;;) { ++ cell = snd_seq_prioq_cell_out(q->timeq, &q->timer->cur_time); ++ if (!cell) + break; +- } ++ snd_seq_dispatch_event(cell, atomic, hop); + } + + /* free lock */ diff --git a/queue-3.18/fs-aio-add-explicit-rcu-grace-period-when-freeing-kioctx.patch b/queue-3.18/fs-aio-add-explicit-rcu-grace-period-when-freeing-kioctx.patch new file mode 100644 index 00000000000..cf7266ca9b4 --- /dev/null +++ b/queue-3.18/fs-aio-add-explicit-rcu-grace-period-when-freeing-kioctx.patch @@ -0,0 +1,92 @@ +From a6d7cff472eea87d96899a20fa718d2bab7109f3 Mon Sep 17 00:00:00 2001 +From: Tejun Heo +Date: Wed, 14 Mar 2018 12:10:17 -0700 +Subject: fs/aio: Add explicit RCU grace period when freeing kioctx + +From: Tejun Heo + +commit a6d7cff472eea87d96899a20fa718d2bab7109f3 upstream. + +While fixing refcounting, e34ecee2ae79 ("aio: Fix a trinity splat") +incorrectly removed explicit RCU grace period before freeing kioctx. +The intention seems to be depending on the internal RCU grace periods +of percpu_ref; however, percpu_ref uses a different flavor of RCU, +sched-RCU. This can lead to kioctx being freed while RCU read +protected dereferences are still in progress. + +Fix it by updating free_ioctx() to go through call_rcu() explicitly. + +v2: Comment added to explain double bouncing. + +Signed-off-by: Tejun Heo +Reported-by: Jann Horn +Fixes: e34ecee2ae79 ("aio: Fix a trinity splat") +Cc: Kent Overstreet +Cc: Linus Torvalds +Cc: stable@vger.kernel.org # v3.13+ +Signed-off-by: Greg Kroah-Hartman + +--- + fs/aio.c | 23 +++++++++++++++++++---- + 1 file changed, 19 insertions(+), 4 deletions(-) + +--- a/fs/aio.c ++++ b/fs/aio.c +@@ -110,7 +110,8 @@ struct kioctx { + struct page **ring_pages; + long nr_pages; + +- struct work_struct free_work; ++ struct rcu_head free_rcu; ++ struct work_struct free_work; /* see free_ioctx() */ + + /* + * signals when all in-flight requests are done +@@ -505,6 +506,12 @@ static int kiocb_cancel(struct kiocb *ki + return cancel(kiocb); + } + ++/* ++ * free_ioctx() should be RCU delayed to synchronize against the RCU ++ * protected lookup_ioctx() and also needs process context to call ++ * aio_free_ring(), so the double bouncing through kioctx->free_rcu and ++ * ->free_work. ++ */ + static void free_ioctx(struct work_struct *work) + { + struct kioctx *ctx = container_of(work, struct kioctx, free_work); +@@ -518,6 +525,14 @@ static void free_ioctx(struct work_struc + kmem_cache_free(kioctx_cachep, ctx); + } + ++static void free_ioctx_rcufn(struct rcu_head *head) ++{ ++ struct kioctx *ctx = container_of(head, struct kioctx, free_rcu); ++ ++ INIT_WORK(&ctx->free_work, free_ioctx); ++ schedule_work(&ctx->free_work); ++} ++ + static void free_ioctx_reqs(struct percpu_ref *ref) + { + struct kioctx *ctx = container_of(ref, struct kioctx, reqs); +@@ -526,8 +541,8 @@ static void free_ioctx_reqs(struct percp + if (ctx->requests_done) + complete(ctx->requests_done); + +- INIT_WORK(&ctx->free_work, free_ioctx); +- schedule_work(&ctx->free_work); ++ /* Synchronize against RCU protected table->table[] dereferences */ ++ call_rcu(&ctx->free_rcu, free_ioctx_rcufn); + } + + /* +@@ -749,7 +764,7 @@ static int kill_ioctx(struct mm_struct * + table->table[ctx->id] = NULL; + spin_unlock(&mm->ioctx_lock); + +- /* percpu_ref_kill() will do the necessary call_rcu() */ ++ /* free_ioctx_reqs() will do the necessary RCU synchronization */ + wake_up_all(&ctx->wait); + + /* diff --git a/queue-3.18/fs-aio-use-rcu-accessors-for-kioctx_table-table.patch b/queue-3.18/fs-aio-use-rcu-accessors-for-kioctx_table-table.patch new file mode 100644 index 00000000000..2e1d2de079b --- /dev/null +++ b/queue-3.18/fs-aio-use-rcu-accessors-for-kioctx_table-table.patch @@ -0,0 +1,87 @@ +From d0264c01e7587001a8c4608a5d1818dba9a4c11a Mon Sep 17 00:00:00 2001 +From: Tejun Heo +Date: Wed, 14 Mar 2018 12:10:17 -0700 +Subject: fs/aio: Use RCU accessors for kioctx_table->table[] + +From: Tejun Heo + +commit d0264c01e7587001a8c4608a5d1818dba9a4c11a upstream. + +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 +Reported-by: Jann Horn +Fixes: db446a08c23d ("aio: convert the ioctx list to table lookup v3") +Cc: Benjamin LaHaise +Cc: Linus Torvalds +Cc: stable@vger.kernel.org # v3.12+ +Signed-off-by: Greg Kroah-Hartman + +--- + fs/aio.c | 19 ++++++++++--------- + 1 file changed, 10 insertions(+), 9 deletions(-) + +--- a/fs/aio.c ++++ b/fs/aio.c +@@ -68,9 +68,9 @@ struct aio_ring { + #define AIO_RING_PAGES 8 + + struct kioctx_table { +- struct rcu_head rcu; +- unsigned nr; +- struct kioctx *table[]; ++ struct rcu_head rcu; ++ unsigned nr; ++ struct kioctx __rcu *table[]; + }; + + struct kioctx_cpu { +@@ -583,9 +583,9 @@ static int ioctx_add_table(struct kioctx + while (1) { + if (table) + for (i = 0; i < table->nr; i++) +- if (!table->table[i]) { ++ if (!rcu_access_pointer(table->table[i])) { + ctx->id = i; +- table->table[i] = ctx; ++ rcu_assign_pointer(table->table[i], ctx); + spin_unlock(&mm->ioctx_lock); + + /* While kioctx setup is in progress, +@@ -760,8 +760,8 @@ static int kill_ioctx(struct mm_struct * + + spin_lock(&mm->ioctx_lock); + table = rcu_dereference_raw(mm->ioctx_table); +- WARN_ON(ctx != table->table[ctx->id]); +- table->table[ctx->id] = NULL; ++ WARN_ON(ctx != rcu_access_pointer(table->table[ctx->id])); ++ RCU_INIT_POINTER(table->table[ctx->id], NULL); + spin_unlock(&mm->ioctx_lock); + + /* free_ioctx_reqs() will do the necessary RCU synchronization */ +@@ -817,7 +817,8 @@ void exit_aio(struct mm_struct *mm) + return; + + for (i = 0; i < table->nr; ++i) { +- struct kioctx *ctx = table->table[i]; ++ struct kioctx *ctx = ++ rcu_dereference_protected(table->table[i], true); + struct completion requests_done = + COMPLETION_INITIALIZER_ONSTACK(requests_done); + +@@ -1003,7 +1004,7 @@ static struct kioctx *lookup_ioctx(unsig + if (!table || id >= table->nr) + goto out; + +- ctx = table->table[id]; ++ ctx = rcu_dereference(table->table[id]); + if (ctx && ctx->user_id == ctx_id) { + percpu_ref_get(&ctx->users); + ret = ctx; diff --git a/queue-3.18/lock_parent-needs-to-recheck-if-dentry-got-__dentry_kill-ed-under-it.patch b/queue-3.18/lock_parent-needs-to-recheck-if-dentry-got-__dentry_kill-ed-under-it.patch new file mode 100644 index 00000000000..146a4e8459d --- /dev/null +++ b/queue-3.18/lock_parent-needs-to-recheck-if-dentry-got-__dentry_kill-ed-under-it.patch @@ -0,0 +1,49 @@ +From 3b821409632ab778d46e807516b457dfa72736ed Mon Sep 17 00:00:00 2001 +From: Al Viro +Date: Fri, 23 Feb 2018 20:47:17 -0500 +Subject: lock_parent() needs to recheck if dentry got __dentry_kill'ed under it + +From: Al Viro + +commit 3b821409632ab778d46e807516b457dfa72736ed upstream. + +In case when dentry passed to lock_parent() is protected from freeing only +by the fact that it's on a shrink list and trylock of parent fails, we +could get hit by __dentry_kill() (and subsequent dentry_kill(parent)) +between unlocking dentry and locking presumed parent. We need to recheck +that dentry is alive once we lock both it and parent *and* postpone +rcu_read_unlock() until after that point. Otherwise we could return +a pointer to struct dentry that already is rcu-scheduled for freeing, with +->d_lock held on it; caller's subsequent attempt to unlock it can end +up with memory corruption. + +Cc: stable@vger.kernel.org # 3.12+, counting backports +Signed-off-by: Al Viro +Signed-off-by: Greg Kroah-Hartman + +--- + fs/dcache.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +--- a/fs/dcache.c ++++ b/fs/dcache.c +@@ -581,11 +581,16 @@ again: + spin_unlock(&parent->d_lock); + goto again; + } +- rcu_read_unlock(); +- if (parent != dentry) ++ if (parent != dentry) { + spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); +- else ++ if (unlikely(dentry->d_lockref.count < 0)) { ++ spin_unlock(&parent->d_lock); ++ parent = NULL; ++ } ++ } else { + parent = NULL; ++ } ++ rcu_read_unlock(); + return parent; + } + diff --git a/queue-3.18/series b/queue-3.18/series index 9471872be7e..e6cc4601b89 100644 --- a/queue-3.18/series +++ b/queue-3.18/series @@ -57,3 +57,9 @@ mac80211-remove-bug-when-interface-type-is-invalid.patch asoc-nuc900-fix-a-loop-timeout-test.patch rcutorture-configinit-fix-build-directory-error-message.patch ima-relax-requiring-a-file-signature-for-new-files-with-zero-length.patch +alsa-pcm-fix-uaf-in-snd_pcm_oss_get_formats.patch +alsa-seq-fix-possible-uaf-in-snd_seq_check_queue.patch +alsa-seq-clear-client-entry-before-deleting-else-at-closing.patch +lock_parent-needs-to-recheck-if-dentry-got-__dentry_kill-ed-under-it.patch +fs-aio-add-explicit-rcu-grace-period-when-freeing-kioctx.patch +fs-aio-use-rcu-accessors-for-kioctx_table-table.patch