Mailing List Archive

Nessus-libraries system PCAP patch with side benefit of fixing Fedora Core 4 segfaults
Hi,



Recently, I tried to build Nessus 2.2.5 & 2.2.6 on a few Fedora Core 4
(ia32) systems with gcc 4.0.1-5, but the resulting code always had the
same problem - when running any plugin that called the built-in libpcap,
the script/plugin would segfault. Although Nessus recovered admirably
from these problems, it still obviously skewed the results
significantly, especially when using synscan as the only portscanner. I
was able to track the segfault to the opt_init() function in
nessus-libraries/libpcap-nessus/optimize.c, but I was unable to track
down the actual problem - opt_init() was called from bpf_optimize(), it
completed, and then the segfault occurred before returning to
bpf_optimize(). That seems to imply a stack problem, but I couldn't
tell where the stack was corrupted. I tried removing all rlimits, just
in case, but that made no difference. I should also say that I didn't
build any of the Nessus code with any of the string overflow protection
definitions that's built into Fedora Core 4.



To see if you have this same problem, grep your nessusd.messages for the
string "SIGSEGV". If this occurs, check to see which plugins caused the
segfault - if you have the same problem as I did, each of the plugins
that crashed use libpcap (like ping_host.nasl, synscan.nes, etc).



To fix this problem, I tried to switch to using the system libpcap
(0.8.3) using the -disable-nessuspcap configure argument, but since
Linux has no read timeout, and since Nessus wasn't calling the libpcap
nonblocking/select functions, that option wasn't viable either (the
calls to bpf_next_tv would block until a packet meeting its filter
criteria was read).



So, I patched libnessus to use the system libpcap in the timeout
workaround way - that is, to make the pcap file descriptor nonblocking,
request the "selectable" file descriptor from libpcap, and then to use
the timeval passed to bpf_next_tv() as the select timeout.



There are a few benefits with this approach - most obviously, it doesn't
crash the Nessus plugins on Fedora Core 4. Whatever was causing that
problem is apparently not a problem with the system libpcap. Second,
the newer libpcap takes advantage of some modern Linux's kernel features
- it uses the newer PF_PACKET sockets for packet capture, and it takes
advantage of kernel socket filtering (if it's enabled in the kernel),
both of which provide performance improvements.



In my patch, I set the code to attempt this approach only if the system
didn't have a native BPF device - this is because read timeouts already
work on BSD systems, and also BSDs will return a valid socket with the
pcap_get_selectable_fd() call, but this socket isn't usable in the same
way it is on other OS'. I haven't tested the code on any non-Linux
systems, so I can't say if it will work on non-Linux Fedora-based
systems.



Still, I think the patch has a lot of merit - even if the segfault
problem encountered by me is a quirk of the gcc I'm using, or a small
bug in the built-in libpcap code, I still think the added benefits of
using a modern pcap on Linux make this patch useful as more than just a
workaround.



I did my best to test this patch against all of the plugins I could,
focusing on the plugins that use pcap functionality. In all cases, the
tests worked as expected. I only tested this on Fedora Core 2
(gcc-3.3.3) and Fedora Core 4 (gcc-4.0.1-5), both with libpcap-0.8.3.



Here is the patch - it patches configure.in, configure, config.h.in and
bpf_share.c, all in nessus-libraries. To use it, apply this patch to a
new nessus-libraries, and build Nessus as usual, but make sure you add
--disable-nessuspcap to your nessus-libraries' configure commandline.



======================/ BEGIN PATCH /======================



diff -r -C 5 nessus-libraries/configure
nessus-libraries-pcap-patch/configure

*** nessus-libraries/configure 2005-10-27 09:29:18.000000000 -0400

--- nessus-libraries-pcap-patch/configure 2005-11-09 05:05:09.000000000
-0500

***************

*** 8333,8342 ****

--- 8333,8385 ----

pcap_dir=""

unset pcap_install

unset pcap_make

unset pcap_clean

unset pcap_distclean

+ echo $ac_n "checking for pcap_get_selectable_fd in -lpcap""...
$ac_c" 1>&6

+ echo "configure:8340: checking for pcap_get_selectable_fd in -lpcap"
>&5

+ ac_lib_var=`echo pcap'_'pcap_get_selectable_fd | sed 'y%./+-%__p_%'`

+ if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set";
then

+ echo $ac_n "(cached) $ac_c" 1>&6

+ else

+ ac_save_LIBS="$LIBS"

+ LIBS="-lpcap $LIBS"

+ cat > conftest.$ac_ext <<EOF

+ #line 8348 "configure"

+ #include "confdefs.h"

+ /* Override any gcc2 internal prototype to avoid an error. */

+ /* We use char because int might match the return type of a gcc2

+ builtin and then its argument prototype would still apply. */

+ char pcap_get_selectable_fd();

+

+ int main() {

+ pcap_get_selectable_fd()

+ ; return 0; }

+ EOF

+ if { (eval echo configure:8359: \"$ac_link\") 1>&5; (eval $ac_link)
2>&5; } && test -s conftest${ac_exeext}; then

+ rm -rf conftest*

+ eval "ac_cv_lib_$ac_lib_var=yes"

+ else

+ echo "configure: failed program was:" >&5

+ cat conftest.$ac_ext >&5

+ rm -rf conftest*

+ eval "ac_cv_lib_$ac_lib_var=no"

+ fi

+ rm -f conftest*

+ LIBS="$ac_save_LIBS"

+

+ fi

+ if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then

+ echo "$ac_t""yes" 1>&6

+ cat >> confdefs.h <<\EOF

+ #define HAVE_PCAP_GET_SELECTABLE_FD 1

+ EOF

+

+ else

+ echo "$ac_t""no" 1>&6

+ fi

+

else

syspcap=""

pcap_flag="-lpcap-nessus"

pcap_install="pcap-install"

pcap_make="pcap-make"

diff -r -C 5 nessus-libraries/configure.in
nessus-libraries-pcap-patch/configure.in

*** nessus-libraries/configure.in 2005-10-27 09:29:18.000000000 -0400

--- nessus-libraries-pcap-patch/configure.in 2005-11-09
05:05:09.000000000 -0500

***************

*** 463,472 ****

--- 463,473 ----

pcap_dir=""

unset pcap_install

unset pcap_make

unset pcap_clean

unset pcap_distclean

+
AC_CHECK_LIB(pcap,pcap_get_selectable_fd,AC_DEFINE(HAVE_PCAP_GET_SELECTA
BLE_FD))

else

syspcap=""

pcap_flag="-lpcap-nessus"

pcap_install="pcap-install"

pcap_make="pcap-make"

diff -r -C 5 nessus-libraries/include/config.h.in
nessus-libraries-pcap-patch/include/config.h.in

*** nessus-libraries/include/config.h.in 2005-04-19
07:04:58.000000000 -0400

--- nessus-libraries-pcap-patch/include/config.h.in 2005-11-09
05:06:04.000000000 -0500

***************

*** 323,327 ****

--- 323,329 ----



#undef HAVE_ACCRIGHTS_IN_MSGHDR

#undef HAVE_CONTROL_IN_MSGHDR



#undef DEBUG_STORE

+

+ #undef HAVE_PCAP_GET_SELECTABLE_FD

diff -r -C 5 nessus-libraries/libnessus/bpf_share.c
nessus-libraries-pcap-patch/libnessus/bpf_share.c

*** nessus-libraries/libnessus/bpf_share.c 2005-07-06
16:48:10.000000000 -0400

--- nessus-libraries-pcap-patch/libnessus/bpf_share.c 2005-11-09
05:05:09.000000000 -0500

***************

*** 942,951 ****

--- 942,960 ----

{

printf("%s\n", errbuf);

return -1;

}



+ #ifdef HAVE_PCAP_GET_SELECTABLE_FD

+ if (pcap_setnonblock(ret, 1, errbuf) < 0)

+ {

+ printf("pcap_setnonblock() failed\n");

+ pcap_close(ret);

+ return -1;

+ }

+ #endif

+

if(pcap_lookupnet(iface, &network, &netmask, 0) < 0)

{

printf("pcap_lookupnet failed\n");

pcap_close(ret);

return -1;

***************

*** 968,987 ****

--- 977,1053 ----

return i;

}







+ #ifdef HAVE_PCAP_GET_SELECTABLE_FD

+ /*

+ * bpf_next_tv() - Modified version to use the tcpdump.org's libpcap
0.7.1+

+ * non-blockable / selectable functionality to
simulate BPF

+ * timeouts

+ *

+ */

+ u_char* bpf_next_tv(int bpf, int * caplen, struct timeval * tv)

+ {

+ u_char * p = NULL;

+ struct pcap_pkthdr *head;

+ int sock;

+ u_char read=0;

+ fd_set rfds;

+ int rv;

+

+ if ((sock=pcap_get_selectable_fd(pcaps[bpf])) < 0)

+ {

+ printf("Failed to get the selectable fd!\n");

+ return NULL;

+ }

+

+ while (!read)

+ {

+ FD_ZERO(&rfds);

+ FD_SET(sock, &rfds);

+

+ if ((rv = select(sock + 1, &rfds, NULL, NULL, tv)) < 0)

+ {

+ if (errno == EAGAIN)

+ continue;

+

+ printf("Select() failed in bpf_next_tv()\n");

+ return NULL;

+ }

+

+ read++;

+

+ if (rv == 0)

+ return NULL;

+

+ if (rv > 0 && FD_ISSET(sock, &rfds))

+ {

+ if ((rv = pcap_next_ex(pcaps[bpf], (struct pcap_pkthdr
**)&head, &p)) < 0)

+ {

+ printf("pcap_next_ex() failed\n");

+ bpf_close(bpf);

+ return NULL;

+ }

+ }

+ }

+

+ *caplen = head->caplen;

+ return p;

+ }

+ #else

u_char* bpf_next_tv(int bpf, int * caplen, struct timeval * tv)

{

u_char * p = NULL;

struct pcap_pkthdr head;





p = (u_char*)pcap_next(pcaps[bpf], &head);

*caplen = head.caplen;

return p;

}

+ #endif





u_char* bpf_next(int bpf, int * caplen)

{

struct timeval tv = {0, 100000};



=======================/ END PATCH /=======================



Hopefully this code is helpful to some of you.



The code could be improved if the configure check for
pcap_get_selectable_fd() would also verify that it didn't get -1 back,
but that's a bit beyond what I know how to do with configure. If
anyone's gotten Nessus 2.2.[56] to run successfully under Fedora Core 4,
I'd be interested in hearing how you did it (I'm especially curious to
know if gcc4 had something to do with the problem.



Brian Costello