[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20080425125230.GA12343@alice>
Date: Fri, 25 Apr 2008 14:52:30 +0200
From: Eric Sesterhenn <snakebyte@....de>
To: Evgeniy Polyakov <johnpol@....mipt.ru>
Cc: netdev@...r.kernel.org
Subject: Re: Slab Corruption with ipv6 and tcp6fuzz
* Evgeniy Polyakov (johnpol@....mipt.ru) wrote:
> Hi.
>
> On Thu, Apr 24, 2008 at 04:27:27PM +0200, Eric Sesterhenn (snakebyte@....de) wrote:
> > i found some local ivp6 network fuzzing tools from the bsd folks
> > today and wanted to add them to my testmachine. When
> > trying one of them (running with user privs) it gave me slab corruption errors.
> > Running http://clem1.be/lf6/tcp6fuzz.c 1 to 5 times
> > always results in errors, strangely using the same seed twice
> > in a row doesnt trigger the warnings again.
> >
> > If there is any more info i can provide please let me know.
>
> $ wget http://clem1.be/lf6/tcp6fuzz.c
> --01:09:26-- http://clem1.be/lf6/tcp6fuzz.c
> => `tcp6fuzz.c'
> Resolving clem1.be... 88.169.180.107
> Connecting to clem1.be|88.169.180.107|:80... failed: Connection refused.
>
> Please post your source here (google can not find it either), if it is
> that easily reproducible, you can be sure, bug will be fixed in a few
> moments.
>
> > [ 57.810370] sock_set_timeout: `tcp6fuzz' (pid 3721) tries to set negative timeout
> > [ 215.102729] =============================================================================
> > [ 215.102786] BUG skbuff_head_cache: Invalid object pointer 0xccd2b520
> > [ 215.102810] -----------------------------------------------------------------------------
> > [ 215.102816]
> > [ 215.102840] INFO: Slab 0xc119c560 used=10 fp=0x00000000 flags=0x40000083
> > [ 215.102868] Pid: 0, comm: swapper Not tainted 2.6.25-03562-g3dc5063 #23
> > [ 215.102880] [<c0177b57>] slab_err+0x47/0x50
> > [ 215.102978] [<c0177bc7>] ? slab_pad_check+0x67/0xe0
> > [ 215.102994] [<c0177c92>] ? check_slab+0x52/0x80
> > [ 215.103010] [<c0179405>] __slab_free+0x1d5/0x2d0
> > [ 215.103024] [<c0179eb0>] kmem_cache_free+0x80/0xe0
> > [ 215.103039] [<c05d91dc>] ? __kfree_skb+0x3c/0x90
> > [ 215.103063] [<c05d91dc>] ? __kfree_skb+0x3c/0x90
> > [ 215.103078] [<c05d91dc>] __kfree_skb+0x3c/0x90
> > [ 215.103090] [<c05d9249>] kfree_skb+0x19/0x30
> > [ 215.103103] [<c0671e3b>] tcp_v6_do_rcv+0x33b/0xcd0
>
> So far can you run kernel with debug turned on and provide output of
> gdb ./vmlinux
> l *(tcp_v6_do_rcv+0x33b)
l *(tcp_v6_do_rcv+0x33b)
Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".
(gdb) l *(tcp_v6_do_rcv+0x33b)
0xc0671e3b is in tcp_v6_do_rcv (net/ipv6/tcp_ipv6.c:1670).
1665 reset:
1666 tcp_v6_send_reset(sk, skb);
1667 discard:
1668 if (opt_skb)
1669 __kfree_skb(opt_skb);
1670 kfree_skb(skb);
1671 return 0;
1672 csum_err:
1673 TCP_INC_STATS_BH(TCP_MIB_INERRS);
1674 goto discard;
Here is the programm itself...
Greetings, Eric
/*
* TCP/IPV6 socket fuzzer.
*
* Copyright (C) 2006, Clément Lecigne
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <sys/uio.h>
void usage(char *);
void randsoopt(int);
void fs(char *, size_t);
void fc(uint32_t, uint32_t, uint32_t, size_t, char *);
unsigned int randaddr(void);
/*
* boucle until we hit a valid socket option
*/
void randsoopt(int sock)
{
unsigned int optval;
int optlen, optname, level, ret, on = rand() % 2;
do
{
switch (rand() % 4)
{
case 0:
level = IPPROTO_IPV6;
break;
case 1:
level = SOL_SOCKET;
break;
case 2:
level = IPPROTO_TCP;
break;
case 3:
level = rand() & 0xFF;
break;
}
if (rand() % 8)
{
optlen = rand() & 0xffff;
optval = randaddr();
}
else
{
/*
* In some cases, kernel excepts that
* optlen == sizeof (int) and that's
* the first bound checking.
*/
optlen = sizeof (int);
optval = (unsigned int)&on;
}
if (rand() % 8)
optname = rand() % 80;
else
optname = rand();
#if 0
/*
* anti well know mbufs exhaustion. (FreeBSD)
*/
if (optname == 25 || optname == IPV6_IPSEC_POLICY ||
optname == IPV6_FW_ADD || optname == IPV6_FW_FLUSH
|| optname == IPV6_FW_DEL || optname == IPV6_FW_ZERO)
continue;
#endif
ret = setsockopt(sock, level, optname, (void *)optval, optlen);
}while(ret == -1);
}
/*
* server fuzzage.
*/
void fs(char *port, size_t ms)
{
int so, ac, one = 1;
struct addrinfo *res, hints;
struct sockaddr_in6 from;
socklen_t fromlen;
char *buf;
struct msghdr m;
struct cmsghdr *c = NULL;
struct iovec io;
buf = malloc(ms);
if (buf == NULL)
{
perror(" - malloc");
return;
}
fromlen = sizeof from;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
getaddrinfo("::1", port, &hints, &res);
m.msg_name = res->ai_addr;
m.msg_namelen = res->ai_addrlen;
m.msg_iov = &io;
so = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (so == -1){
perror("sock");
exit(EXIT_FAILURE);
}
randsoopt(so);
if (bind(so, res->ai_addr, res->ai_addrlen) < 0){
perror("bind");
exit(EXIT_FAILURE);
}
if (listen(so, 0) < 0){
perror("listen");
exit(EXIT_FAILURE);
}
while(1)
{
randsoopt(so);
ac = accept(so, (struct sockaddr *)&from, &fromlen);
if (ac == -1){
perror("accept");
continue; /* warn but continue */
}
randsoopt(ac);
/* make ac non-blockant */
ioctl(ac, FIONBIO, &one);
/* different receiving ways */
switch(rand() % 3)
{
case 0: /* basic read */
read(ac, buf, rand() % ms);
break;
case 1: /* recvfrom */
recvfrom(ac, buf, rand() % ms, 0,
(struct sockaddr *)&from, &fromlen);
break;
case 2: /* recvmsg */
m.msg_iovlen = (rand() % 2) ? rand() : 1;
m.msg_controllen = (rand() % 2) ? CMSG_LEN(rand() % ms) : 0;
if (m.msg_controllen)
{
c = (struct cmsghdr *)malloc(m.msg_controllen);
m.msg_control = c;
c->cmsg_level = (rand() % 2) ? IPPROTO_IPV6 : rand();
c->cmsg_type = (rand() % 2) ? rand() % 255 : rand();
c->cmsg_len = (rand() % 2) ? m.msg_controllen : rand();
}
else
{
m.msg_control = (rand() % 5) ? NULL : (void*)randaddr();
}
m.msg_flags = rand();
io.iov_len = (rand() % 2) ? rand() : rand() % ms;
recvmsg(ac, &m, (rand() % 2) ? 0 : rand());
break;
}
close(ac);
}
free(buf);
freeaddrinfo(res);
}
/*
* client fuzzage.
*/
void fc(uint32_t count, uint32_t occ, uint32_t opts, size_t ms, char *port)
{
int so, j, cc, one = 1;
uint32_t try;
u_int32_t i, a;
struct addrinfo *res, hints;
char *buf;
struct msghdr m;
struct cmsghdr *c = NULL;
struct iovec io;
/* XXX: wait server */
usleep(500);
buf = malloc(ms);
if (buf == NULL)
{
perror(" - malloc");
return;
}
signal(SIGPIPE, SIG_IGN);
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
getaddrinfo("::1", port, &hints, &res);
m.msg_name = res->ai_addr;
m.msg_namelen = res->ai_addrlen;
m.msg_iov = &io;
for (i = 0; i < occ; i++)
{
printf("%d\n", i);
try = 0;
do{
so = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
try++;
}while(so == -1 && count != try);
/* make socket non-blockant */
ioctl(so, FIONBIO, &one);
try = 0;
do{
cc = connect(so, res->ai_addr, res->ai_addrlen);
try++;
}while(cc == -1 && count != try);
randsoopt(so);
for (a = 0; a < opts; a++)
{
try = 0;
do
{
switch(rand() % 3)
{
case 0:
cc = write(so, buf, rand() % ms);
break;
case 1:
cc = sendto(so, buf, rand() % ms, MSG_DONTWAIT,
(struct sockaddr *)&res->ai_addr, res->ai_addrlen);
break;
case 2:
m.msg_iovlen = (rand() % 2) ? rand() : 1;
m.msg_controllen = (rand() % 2) ? CMSG_LEN(rand() % ms) : 0;
m.msg_flags = MSG_DONTWAIT;
if (m.msg_controllen)
{
c = (struct cmsghdr *)malloc(m.msg_controllen);
m.msg_control = c;
c->cmsg_level = (rand() % 2) ? IPPROTO_IPV6 : rand();
c->cmsg_type = (rand() % 2) ? rand() % 255 : rand();
c->cmsg_len = (rand() % 2) ? m.msg_controllen : rand();
}
else
{
m.msg_control = (rand() % 5) ? NULL : (void*)randaddr();
m.msg_controllen = (rand() % 2) ? rand() : 0;
}
io.iov_len = (rand() % 2) ? rand() : rand() % ms;
cc = sendmsg (so, &m, MSG_DONTWAIT);
}
if (c != NULL)
{
free(c);
c = NULL;
}
try++;
}while(cc == -1 && count != try);
}
close(so);
}
free(buf);
freeaddrinfo(res);
return;
}
/*
* return a random address
*/
unsigned int randaddr(void)
{
int stack;
char *p = malloc(1);
unsigned int heap = (unsigned int)p;
free(p);
switch (rand() % 4)
{
case 0:
return (heap + (rand() & 0xFFF));
case 1:
return ((unsigned int)&stack + (rand() & 0xFFF));
case 2:
return (0xc0000000 + (rand() & 0xFFFF));
case 3:
return (rand());
}
return (0);
}
/*
* usage
*/
void usage(char *prog)
{
printf("usage: %s [-r seed] [-c sendto-timeout] [-m maxsize]\n"
" [-o maxsetsockopt] [-n occ] [-p tcp-port]\n", prog);
exit(1);
}
int main(int ac, char **av)
{
int32_t occ, count, opts;
u_int32_t seed;
size_t maxsize;
char c;
pid_t pid;
char *port = "5000";
/* default values */
seed = getpid();
count = 50;
occ = 100;
maxsize = 4096;
opts = 10;
while ((c = getopt(ac, av, "r:n:c:p:m:o:")) != EOF)
{
switch (c)
{
case 'r':
seed = atoi(optarg);
break;
case 'n':
occ = atoi(optarg);
break;
case 'c':
count = atoi(optarg);
break;
case 'p':
port = optarg;
break;
case 'm':
maxsize = atoi(optarg);
break;
case 'o':
opts = atoi(optarg);
break;
default:
usage(av[0]);
break;
}
}
printf(" + using seed : %d\n", seed);
srand(seed);
puts(" + forking.");
pid = fork();
switch (pid)
{
case -1:
perror(" - fork");
exit(EXIT_FAILURE);
case 0:
/* client fuzzer */
fs(port, maxsize);
break;
default:
/* server */
fc(count, occ, opts, maxsize, port);
break;
}
/* client finished, kill the serv */
kill(pid, SIGKILL);
puts(" + fuzzing finished, your kernel is alive.");
exit(EXIT_SUCCESS);
}
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists