tcpdump mailing list archives

Bug in libpcap: savefile.c / get_selectable_fd()


From: "Shaked, Nitzan" <nshaked () paypal com>
Date: Tue, 17 Mar 2009 22:33:36 -0000

Hello all
 
I hope this mailing list is active, and that this is the right place to post to. In any case, I believe I have 
discovered a bug: get_selectable_fd() does not work well when used with pcap_open_offline(). The symptom is that if you 
use, as was the poet's intention, get_selectable_fd()'s return value in a call to select() and then use pcap_next() to 
get a packet, you will fail to read the last few packets if reading from a pipe (for example: reading from stdin when 
it's piped).
 
The reason is that savefile.c implements the read_op using fread, which is stdio and uses *buffering*, while select() 
is kernel IO and is unaware of the buffers in stdlib. So while the last packets are inside stdlib's buffer, select() 
will not see the fd as "ready for read", will never return, and you will never see these packets.
 
(Assume that the pipe doesn't close -- it stays open but simply has no more input. For example: it's the output of 
tcpdump -w)
 
In general, it's a bad thing to mix buffered IO (stdlib, such as fread,fread,fseek, etc) with kernel io 
(read/write/seek/select, etc).
 
There are 2 obvious solutions, with pros and cons each, and possibly more solutions as well:
 
1) Use setvbuf( fp, NULL, _IONBF, 0 ) inside pcap_fopen_offline. I tried this and it works, on both Solaris and 
Red-Hat. There are several drawbacks which I will detail later.
 
2) Stop using stdlib in savefile.c: use read instead of fread. There are only a handful of places to change, and it 
seems easy enough. The "downsize" is that you can't simply replace a call to fread with a call to read, because read() 
may be interrupted, so you need to loop if you get EINTR. Another downside is that you have to remove 
pcap_fopen_offline(), and replace it with something like pcap_fdopen_offline(). Again: no stdio at all.
 
The downsides for (1) above:
 
a) setvbuf(), in glibc, does what you want:removes buffers *also from the input, that is: if you use fread()*. It is 
*NOT* guaranteed to be the case in the official stdio documentation. All the documentation I found only talks about 
fwrite() and flushing. So it works for glibc, but maybe not for other stdio libs.
 
b) The documentation states that you must call setvbuf() prior to doing any IO on the FILE* object (but after having 
fopen'ed it, of course). In pcap_fopen_offline() we don't know whether somebody has done any IO on the FILE* object we 
are passed. It works well in glibc, again, because it zeros its read pointer, write point, in buf, out buf, the works. 
It may not, again, work well in other stdio libs, or may not work well if we've done some reading which is still 
buffered (but not consumed) even in glibc. It *does* work well if you, right on program start, try to 
pcap_open_offline( "-", errbuf ) without reading from stdin, but that's the only case I checked.
 
... so: distinguished forum: what do you think? Is the description enough? Should I provide a patch? Should I open a 
bug in sf.net?
 
Best,
Nitzan
-
This is the tcpdump-workers list.
Visit https://cod.sandelman.ca/ to unsubscribe.


Current thread: