[pso] [T2 Linux] Race conditions in exemplul de char device din enuntul temei

Razvan Deaconescu razvan.deaconescu at cs.pub.ro
Tue Aug 26 10:18:09 EEST 2008


On Wed, 2008-08-20 at 21:11 +0300, Stefan Bucur wrote:
> Salut!
> 
> Poate nu am luat in considerare toate aspectele, insa am impresia ca
> exemplul de char device de conversie upper-case dat ca punct de
> pornire este gresit pentru ca permite situatii de race (nu neaparat
> pt. sisteme SMP). Structura:
> 
> struct case_dev {
>     struct cdev cdev;
>     char buffer[BUFFER_SIZE];
>     unsigned put, get;
>     atomic_t fill;
>     wait_queue_head_t wq_reads, wq_writes;
> } devs[CASE_MAX_MINORS];
> 
> este sincronizata doar la nivelul campului fill, incat codul urmator
> poate genera o situatie de race:
> 
> static int case_read(struct file *file, char *user_buffer,
>         size_t size, loff_t *offset)
> {
>         int i=0;
> 	struct case_dev *dev=(struct case_dev*)file->private_data;
> 
> 
> 	if (file->f_mode&O_NONBLOCK) {
> 		if (atomic_read(&dev->fill) == 0)
> 			return -EAGAIN;
> 	} else {
> 		if (wait_event_interruptible(dev->wq_reads, atomic_read(&dev->fill) > 0))
>                         return -ERESTARTSYS;
>         }
> 
>         while ((atomic_read(&dev->fill)>0) && size) {
>                 if (put_user(dev->buffer[dev->get], &user_buffer[i]))
>                         return -EFAULT;
>                 dev->get++; dev->get%=BUFFER_SIZE;
>                 i++; size--; atomic_dec(&dev->fill);
>         }
> 
>         if (atomic_read(&dev->fill) < BUFFER_SIZE)
>                 wake_up(&dev->wq_writes);
> 
>         return i;
> }
> 
> Aici, presupunem ca sunt 2 procese care citesc simultan din device si
> dev->fill = 1. Primul proces reuseste sa intre in bucla while, apoi
> pp. ca kernel-ul este preemptat, si al doilea proces intra in while
> (faptul ca dev->fill este atomica nu ne ajuta cu nimic aici), si
> rezultatul net este ca se va citi de 2 ori din buffer si
> atomic_dec(&dev->fill) va fi executat de doua ori, starea device-ului
> devenind una invalida.
> 
> Acesta e doar un exemplu de race - si dev->get++ poate genera un race
> daca preemptarea are loc in mijlocul incrementarii.
> 
> Solutia evidenta mi se pare folosirea unui spinlock care sa protejeze
> buffer-ul si variabilele de stare asociate, insa as dori sa stiu daca
> exista ceva ce-mi scapa mie si rationamentul de mai sus e gresit.

Da, ai dreptate, poate aparea un race. In mod normal, tinand cont de
contextul actual solutia este folosirea unor semafoare care sa impuna ca
un singur proces sa aiba la un moment dat acces la read/write.

Mai mult, solutia poate fi si mai simpla. Pe vremea mea (TM) am pus o
intrebare cu legatura pe lista de discutii[1]. Este vorba cu de
paragraful cititul/scrisul concurent :-) Pe vremuri scriam mail-uri
lungi nu eram asa succint ca acum (ha! :-P).

Pana la urma am observat din test[2] ca solutia se rezolva prin design.
Faptul ca open nu permite accesul simultan a mai multor procese la
dispozitiv garanteaza evitarea conditiilor de cursa.

Razvan

[1] http://cursuri.cs.pub.ro/pipermail/pso/2005-March/000725.html
[2] http://cursuri.cs.pub.ro/pipermail/pso/2005-March/000726.html



More information about the pso mailing list