oss-sec mailing list archives
sound driver Conditional competition
From: luo <a4651386 () 163 com>
Date: Tue, 16 Jan 2018 15:21:19 +0800 (CST)
PRODUCT: linux kernel VERSION: Most versions.some deadlock ,some uaf.2.6。I tested 2.6 versions, 3.10 versions, and 4.12 PROBLEMTYPE: deadlock or uaf REFERENCES:https://github.com/torvalds/linux/commit/b3defb791b26ea0683a93a4f49c77ec45ec96f10 DESCRIPTION: This vulnerability, which belong to UAF caused by race conditions, can impact the majority of linux distribution(audio system). In file seq_clientmgr.c, function snd_seq_write and snd_seq_ioctl_set_client_pool can cause conditional competition problems when multi-thread is used. snd_seq_write calls snd_seq_cell_alloc to allocate memories for cell from client->pool. When pool is exhausted, schedule is called to switch current thread to another thread, and add current thread to a queue for waiting. snd_seq_ioctl_set_client_pool calls snd_seq_pool_mark_closing to set client->pool->closeing to 1, in order to prevent re-entrant. It also calls snd_seq_queue_client_leave_cells to release cell. And it then calls snd_seq_pool_done, first to release pool and allocate new pool and second to set client->pool->closeing to 0. Function wake_up is both called in snd_seq_queue_client_leave_cells and snd_seq_pool_done, to wake up the thread in the waiting queue mentioned above, avoiding the use of any wild pointer. All is seemed to be well designed , but there is a trick: -- Thread A -- step 1: A calls snd_seq_write to exhaust pool. step 2: snd_seq_write calls func schedule to schedule threads, now go to Thread B. -- Thread B -- step 1: B calls snd_seq_ioctl_set_client_pool. step 2: snd_seq_ioctl_set_client_pool calls snd_seq_pool_mark_closing. snd_seq_pool_mark_closing sets client->pool->closeing to 1. step 3: Then snd_seq_ioctl_set_client_pool calls snd_seq_queue_client_leave_cells. snd_seq_queue_client_leave_cells release the memories of cells. snd_seq_queue_client_leave_cells calls wake_up, now back to Thread A. -- Back To Thread A -- step 1: A will find out that client->pool->closeing is 1, so snd_seq_cell_alloc fails. step 2: Returning from snd_seq_cell_alloc to snd_seq_write. snd_seq_write also fails. step 3: A now call snd_seq_ioctl_set_client_pool. step 4: snd_seq_ioctl_set_client_pool calls snd_seq_pool_mark_closing. snd_seq_pool_mark_closing sets client->pool->closeing to 1 again. step 5: Then snd_seq_ioctl_set_client_pool calls snd_seq_queue_client_leave_cells. cell is already release by B. And because no thread is in waiting queue, so wake_up will not be called. step 6: Then snd_seq_ioctl_set_client_pool calls snd_seq_pool_done. snd_seq_pool_done release pool and allocate new pool. snd_seq_pool_done sets client->pool->closeing to 0. Now it's become reentrant. step 8: So after a call to snd_seq_ioctl_set_client_pool, pool is new. Thread A can call snd_seq_write many times to exhaust the memories of pool. Then A go to sleep, now switch to thread B. -- Back To Thread B -- step 1: Back to snd_seq_queue_client_leave_cells, after previous call to wake_up. step 2: Return to snd_seq_ioctl_set_client_pool. snd_seq_ioctl_set_client_pool call snd_seq_pool_done. snd_seq_pool_done release and allocate new pool. now client->pool->closeing is already 0, and pool is new. -------------------------------------------------------------------- Now you see, the pool allocated by thread A is now released by thread B. And thread B allocate new pool, which is the 3rd pool. But in thread A, in snd_seq_cell_alloc called by snd_seq_write, the pool is actually the 2cd pool, and meet a dead loop: while (pool->free == NULL && ! nonblock && ! pool->closing) Note the 2cd pool is released by thread B in B's snd_seq_ioctl_set_client_pool. Further more, if serveral threads switch between sechedule and wake_up, there will be more obvious sequelae. ---------------------------------------------------- call stack: thread a: -> snd_seq_write -> snd_seq_client_enqueue_event -> snd_seq_event_dup -> snd_seq_cell_alloc -> schedule -> thread b thread b: -> snd_seq_ioctl_set_client_pool -> snd_seq_pool_mark_closing (set closeing to 1) -> snd_seq_queue_client_leave_cells (release cell) -> wake_up -> thread a thread a: -> snd_seq_ioctl_set_client_pool -> snd_seq_pool_mark_closing (set closeing to 1 again) -> snd_seq_queue_client_leave_cells (already release cell by thread b) -> snd_seq_pool_done (release pool and allocate new pool, 2cd pool; set closeing to 0) -> snd_seq_write -> snd_seq_client_enqueue_event -> snd_seq_event_dup -> snd_seq_cell_alloc -> schedule -> thread b thread b: back to snd_seq_queue_client_leave_cells, after func wake_up -> snd_seq_queue_client_leave_cells -> snd_seq_pool_done (release pool and allocate new pool, 3rd pool; set closeing to 0) (leave 2cd pool's cell unhandled) -> wake_up -> thread a: thread a: -> snd_seq_cell_alloc: while (pool->free == NULL && ! nonblock && ! pool->closing) meet dead loop, now pool in thread a is the 2cd pool, has been released, now is a wild pointer. ---EOF--- At 2018-01-12 09:24:58, "kseifried () redhat com" <kseifried () redhat com> wrote:
I'll need some details: [PRODUCT]: [VERSION]: [PROBLEMTYPE]: [REFERENCES]: [DESCRIPTION]: problemtype ideally the CWE identifier (http://cwe.mitre.org) and description includes product, version affected, description of problem, affected component, impact, etc. The references needs to be a public URL with details on the issue, if it's embargoed I'll need a URL where you plan to publish, thanks. No key found so sending plaintext. On 2018-01-11 06:19 PM, luo wrote: -- Kurt Seifried -- Red Hat -- Product Security -- Cloud PGP A90B F995 7350 148F 66BF 7554 160D 4553 5E26 7993 Red Hat Product Security contact: secalert () redhat com
Attachment:
competition.c
Description:
Current thread:
- sound driver Conditional competition luo (Jan 16)
- Re: sound driver Conditional competition Marcus Meissner (Jan 16)
- Re: sound driver Conditional competition Kurt Seifried (Jan 16)
- Re: sound driver Conditional competition Marcus Meissner (Jan 16)