Re: /dev/random and nonblocking I/O bug ?

Natapov Gleb (gnatapov@qualcomm.com)
Sun, 17 Oct 1999 09:02:01 +0200


tytso@mit.edu wrote:
>
> Date: Tue, 12 Oct 1999 16:09:10 +0200
> From: Natapov Gleb <gnatapov@qualcomm.com>
>
> It seems that read from /dev/random can block even if the device was
> opened with O_NONBLOCK flag.
>
> Umm, no, it doesn't block for me....

I can recreate this bug on each and every Linux box.
If you want see this program blocks you should check the following
things:
1) The entropy pool should be full (this condition is not essential for
bug to appear, but it just increase probability to have context switch
while execution of random_read)
2) You must not use any device that generate interrupts while running
the program. Each interrupt cause add_interrupt_randomness function to
be called and this function wakes up all processes form random_read_wait
queue. Thus, if you have any device that constantly generate interrupts
(for instance you download something from the network, or listening to
mp3), you can't notice that process moves from running queue and back
because it does this very very fast.

I'd, actually, had problem recreate this bug at my home computer until
i've found that one program, that I run in startup, make my ide1
controller (I have cdrom there) generate interrupt several time per
second. After killing this program the bug was easily recreatable.

>
> I wrote the following program:
> =================================================================
> #include <errno.h>
> #include <unistd.h>
> #include <sys/types.h>
> #include <sys/stat.h>
> #include <fcntl.h>
> #include <stdio.h>
>
> void main()
> {
> int randfd;
> char c;
> if( (randfd = open("/dev/random", O_RDONLY | O_NONBLOCK)) >= 0){
> while(1){
> if(read(randfd, &c, 1) != 1){
> if( errno != EWOULDBLOCK && errno != 0 )
> fprintf(stderr, "read error form random device");
> break;
> }
> fprintf(stderr,".");
> }
> }
> }
> =====================================================================
>
> This program is broken. Once you exhaust the pool of available
> entropy, read will return 0, and errno will be set to EAGAIN (aka
> EWOULDBLOCK).
>
Why do you think that this program is broken. It checks errno if read
failed to read 1 byte and if errno is EWOULDBLOKC it exits. This program
shouldn't block anyway.

> If the random pool is empty and O_NONBLOCK is set it returns EAGAIN (as
> we are expecting) else it calls extract_entropy().
>
> No, it doesn't return EAGAIN; it breaks from the while loop, and after
> the whole loop current->state is set back to TASK_RUNNING before it
> returns.
>
Yes, you are right, but I was talking about my program. It tries to read
only one byte of data and if one byte is not available it breaks from
the while loop, current->state is set to TASK_RUNNING and EAGAIN is
returned.

Anyway, If you can't recreate this bug on you computer lets look at the
code:
This is the pseudo code for random_read and extract_entropy (I've
dropped not relevant details).

random_read(...,char * buf, size_t nbytes,...)
{
add current to random_read_wait
while(nbytes > 0){
current->state = TASK_INTERRUPTIBLE;
if (there is no enough entropy in entropy pool){
if(nonblocking i/o)
breaks from loop
else
scheduler();
}
/* If we are here - there is at least one byte of entropy in
the entropy pool and current->state == TASK_INTERRUPTIBLE*/
call extract_entropy;
}
current->state = TASK_RUNNING;
remove current from random_read_wait
return;
}

extract_entroy(...,char * buf,...)
{
write available entropy to buffer;
/*XXX*/
if(current->need_resched == 1)
scheduler();
}

If at the point marked /*XXX*/ time interrupt is happened and changes
current->nedd_resched to 1 the process goes to sleep until next
non-timer interrupt.

Gleb.

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/