Mail archive
acf

Re: [Acf] haserl and luasocket

From: Natanael Copa <natanael.copa_at_gmail.com>
Date: Sat, 19 Apr 2008 14:08:30 +0200

On Thu, 2008-04-17 at 16:56 -0400, Nathan Angelacos wrote:
>
> Mika Havela wrote:
> > Hi Nathan.
> > Ncopa created a apk on luasocket. I assume you know what it's used for.
> > For the moment, I had some plans on using it for getting BGP
> > information to display for the user.
> > You can do 'telnet localhost bgpd' to see BGP status. You will need to
> > enter password and some commands, but I was hoping to create
> > acf-quagga to do it for us. But for this I need to communicate with
> > the socket.
> > So I started with a easy code (example from PIL book on page 83) on
> > how you download a web-page.
> > This works just fine when you do it on console 'lua -l socket' and run
> > your commands.
> > I made a testscript that downloads a page, counts the size and outputs
> > the size on the screen.
> >
> > But... when doing this by using haserl, something goes wrong.
> >
> > dmesg and /var/log/messages complains about
> >
> > Apr 8 10:52:25 bsn2vpnc user.info kernel: acf[21775]: segfault at
> > 00c0c056 eip 56318e03 esp 5fcb3a90 error 6
> > Apr 8 10:52:25 bsn2vpnc user.alert kernel: grsec: From 10.82.3.201:
> > signal 11 sent to /usr/share/acf/www/cgi-bin/acf[acf:21775]
> > uid/euid:0/0 gid/egid:0/0, parent
> > /usr/sbin/mini_httpd[mini_httpd:1580] uid/euid:65534/65534
> > gid/egid:65534/65534
> >
> > I have tried to install a older haserl but it doesn't seem to help.
> >
> > It's the send() and receive() commands that causes lua/haserl/whatever
> > to go wrong.
> >
> > Do you have any suggestions on what could be wrong?
> >
> > luasocket is found on Alpine 1.7.16 (and dev.alpinelinux.org)
> >
> > <<mika>>
> >
>
>
> Ok, documenting what I know so far. This should probably be copied on
> the haserl mailing list but...
>
> Using Haserl 0.9.25_rc1, I was able to duplicate the problem.
>
>
> The segfault is in a memory overwrite in the socket library, but only in
> haserl. When running test.lua from lua itself, valgrind is as happy as
> can be.
>
> The problem happens in line 95 of h_lua.c (lua_doscript function)
>
> 1. Wrote another program to build the lua environment just like haserl
> does, and the problem doesn't happen.
>
> 2. Did a haserl -debug on my haserl script, and ran through lua. The
> segfault didn't happen (valgrind was happy) - so the haserl parser isn't
> making garbage.
>
> 3. Made the test program 'include" all the includes from h_lua.c,
> including config.h, in case there was some funny include mess. Test
> program did not fail.
>
> 4. hacked haserl to not load the library, not load environ variables, or
> anything. Segfault happened.
>
> 5. Modified lua_doscript to run my test code (e.g. I took main() from my
> test program, and replaced the guts of lua_doscript. So lua_doscript
> itself opened a new lua state, read the script from stdin, and executed
> it. The Segfault happened.
>
> Number 5 is interesting. It means that embedding the test program (that
> works) inside of haserl causes the segfault. Yet the test program did
> not use any haserl functions!
>
> At this point I'm a little lost as to why the problem happens, but
> here's the documentation on what was done so far.

I'll append some ideas. To me this smells buffer-overflow-by-one or
similar. The SSP or PaX normally catches those early but if you allocate
a struct and overflow it internally, its impossible to catch those.

 ==892== by 0x403C4B7: (within /usr/lib/liblua5.1.so.0.0.0)
 ==892== by 0x40383A3: lua_pcall (in /usr/lib/liblua5.1.so.0.0.0)
 ==892== by 0x804CD30: lua_doscript (h_lua.c:93)
 ==892== by 0x804DEBC: main (haserl.c:881)
 ==892==
 ==892== Invalid write of size 8
 ==892== at 0x402B042: timeout_markstart (in /usr/lib/liblua5.1-socket.so.2.0.0)
 ==892== by 0x402B797: buffer_meth_send (in /usr/lib/liblua5.1-socket.so.2.0.0)
 ==892== by 0x402D492: (within /usr/lib/liblua5.1-socket.so.2.0.0)
 ==892== by 0x403C911: (within /usr/lib/liblua5.1.so.0.0.0)
 ==892== by 0x4046E12: (within /usr/lib/liblua5.1.so.0.0.0)
 ==892== by 0x403CDAF: (within /usr/lib/liblua5.1.so.0.0.0)
 ==892== by 0x4038540: (within /usr/lib/liblua5.1.so.0.0.0)
 ==892== by 0x403C452: (within /usr/lib/liblua5.1.so.0.0.0)
 ==892== by 0x403C4B7: (within /usr/lib/liblua5.1.so.0.0.0)
 ==892== by 0x40383A3: lua_pcall (in /usr/lib/liblua5.1.so.0.0.0)
 ==892== by 0x804CD30: lua_doscript (h_lua.c:93)
 ==892== by 0x804DEBC: main (haserl.c:881)
 ==892== Address 0x10 is not stack'd, malloc'd or (recently) free'd

 timeout_markstat in socket has a problem

From tcp.h:
typedef struct t_tcp_ {
    t_socket sock;
    t_io io;
    t_buffer buf;
    t_timeout tm;
} t_tcp;

From bufffer.h
/* buffer control structure */
typedef struct t_buffer_ {
    double birthday; /* throttle support info: creation time, */
    size_t sent, received; /* bytes sent, and bytes received */
    p_io io; /* IO driver used for this buffer */
    p_timeout tm; /* timeout management for this buffer */
        size_t first, last; /* index of first and last bytes of stored data */
        char data[BUF_SIZE]; /* storage space for buffer data */
} t_buffer;
typedef t_buffer *p_buffer;

From tcp.c:
static int meth_accept(lua_State *L)
{
    ....
    if (err == IO_DONE) {
        p_tcp clnt = (p_tcp) lua_newuserdata(L, sizeof(t_tcp));
        auxiliar_setclass(L, "tcp{client}", -1);
        /* initialize structure fields */
        socket_setnonblocking(&sock);
        clnt->sock = sock;
        io_init(&clnt->io, (p_send) socket_send, (p_recv) socket_recv,
                (p_error) socket_ioerror, &clnt->sock);
        timeout_init(&clnt->tm, -1, -1);
        buffer_init(&clnt->buf, &clnt->io, &clnt->tm);
        return 1;
    } else {
    ...


So we are looking fot the 'tm' pointers of the buf struct in the initialization.
Upon initializon it seems like the allocated p_tcp struct clnt initalises

clnt->buf->tm = clnt->tm (they are pointing to same address)

what i find interesting in this structs is that the clnt->buf->data
comes exactly before clnt->tm, so if the buf->data is overflowed by one
the clnt->tm will point on a bogus place and PaX won't be able to catch
it until you do something with clnt->tm. PaX does catch something when
touching clnt->buf->tm and since this point to clnt->tm i would believe
that its the buf->data thats overflowed by few some place.

To catch the place where the overflow occurs you could rearrange the
data struc so the buffer comes last in the allocated memoryblock. this
will allow PaX (valgrind?) to catch the overflow exactly when it
occours.

diff -ru luasocket-2.0.2.orig/src/tcp.h luasocket-2.0.2/src/tcp.h
--- luasocket-2.0.2.orig/src/tcp.h 2008-04-19 12:03:15 +0000
+++ luasocket-2.0.2/src/tcp.h 2008-04-19 12:03:46 +0000
_at_@ -25,8 +25,8 @@
 typedef struct t_tcp_ {
     t_socket sock;
     t_io io;
- t_buffer buf;
     t_timeout tm;
+ t_buffer buf;
 } t_tcp;
 
 typedef t_tcp *p_tcp;


When buffer is overflowed now, you will touch unallocated space rather
than overwrite tcp->tm which should trigger PaX/valgrind immediatly.

-nc
Received on Sat Apr 19 2008 - 14:08:30 GMT