Summary:ASTERISK-08670: EAGI buffer overflow in IPC corrupts sound transfer
Reporter:Danijel (devil_slayer)Labels:
Date Opened:2007-01-27 14:48:01.000-0600Date Closed:2007-11-06 19:06:36.000-0600
Versions:Frequency of
Environment:Attachments:( 0) eagi_proxy.c
Description:This may be a bug or a feature depending on how you look at it. It would be nice to fix it in some perceivable future.

The problem is that because of the lack of buffering on part of Asterisk, the EAGI starts skipping samples if the communication pipe gets full. Therefore, if the EAGI client doesn't read the data fast enough, it gets irrecoverably lost.

The solution to this is for the EAGI client program to implement it's own buffer and read the pipe as fast as possible, possibly in a separate thread. This can be tricky in some situation and since all the AGI programs should be small and simple it would be nice if this wasn't necessary.

Luckily, a quick glance at the source code reveals a simple solution. It should be easy to create a buffer inside Asterisk and allow the client EAGI programs to read the pipe at their own pace...

The explanation follows in additional information section:


The piece of code related to this matter is in res_agi.c function static enum agi_result run_agi(...):

/* If it's voice, write it to the audio pipe */
if ((agi->audio > -1) && (f->frametype == AST_FRAME_VOICE)) {
/* Write, ignoring errors */
write(agi->audio, f->data, f->datalen);

Search for the comments to find the exact position in the file.

The agi->audio pipe was set to non-blocking. This means that when the pipe gets filled up (and pipes have pretty small buffers, fitting less than a sec of sound) the write function fails with EAGAIN errno.

As it is stated in the comment, the wise programmer decided to ignore all the errors, which means that is the pipe gets full, the data just doesn't get copied.

The solution consist of two parts. The tricky part is to implement a robust FIFO buffer. The trivial part is to monitor this particular write statement and if some data doesn't get copied it should be stored on the buffer. Naturally, the buffer has to be emptied before new data can be sent through the pipe again.
Comments:By: Serge Vecher (serge-v) 2007-02-01 08:32:32.000-0600

thanks for the report and good analysis. This leans towards the feature end of the spectrum, I would think. Are you able to muster a patch as per your idea of a buffer within Asterisk? If yes, please post it, if not, you will have to take this issue to the asterisk-dev mailing list, where you could perhaps could get some pointers on what kind of solution would be appropriate....

By: Serge Vecher (serge-v) 2007-03-14 08:35:44

devil_slayer: are you able to produce that patch?

By: Danijel (devil_slayer) 2007-03-14 19:40:01

Currently, I don't have the time to get into it. I will keep monitoring this problem and will respond when I find some free time.

For now, I solved it by doing the fix inside the EAGI client app and it works perfectly.

By: Danijel (devil_slayer) 2007-08-21 07:47:57

I made a workaround to the problem I've described here and it seems to be most reasonable solution at this time. The program I posted above is a proxy that allows the same functionality of EAGI but on TCP/IP sockets. It's different from FastAGI in that it also sends the sound from asterisk through a second socket. It creates 3 threads and has a big enough internal buffer (1 minute) that no sound is lost if the signal is not read immediately. Everything else is described in the file.

I'm not sure what to do with the file, but if anyone reads this, they may find a proper place for it in the project.

By: Digium Subversion (svnbot) 2007-11-06 19:06:36.000-0600

Repository: asterisk
Revision: 89077

A   trunk/contrib/utils/eagi_proxy.c

r89077 | tilghman | 2007-11-06 19:06:36 -0600 (Tue, 06 Nov 2007) | 6 lines

Add contributed EAGI proxy, which provides FastAGI functionality for EAGI, while also
buffering the audio stream.
Reported by: devil_slayer
Patch by: devil_slayer
Closes issue ASTERISK-8670