Kernel oops during netfilter memory allocation

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hey there,

I'm currently debugging a kernel oops with kernel 2.6.21.7 that occurs
from time to time with our netfilter accounting module ipt_ACCOUNT 
(http://www.intra2net.com/de/produkte/opensource/ipt_account/).

What basically happens is that the module allocates memory
inside an interrupt handler like many other netfilter modules do.
Except that it does it very often if you have a large, busy network.

To me it looks like I found a bug in the kernel memory handler.
I've written a minimalistic netfilter module ipt_CRASH that
just allocates and frees memory to demonstrate the problem.

Here's a screenshot of the oops:
http://img231.imageshack.us/img231/9064/kerneloopsiptcrashzb1.jpg

Another shot of it with a "tainted" kernel:
http://img529.imageshack.us/img529/6681/kerneloopsey1.jpg

I would have included the backtrace as text, but unfortunately
the USB serial console dies before it outputs anything useful.

You need a fast machine with lots of RAM to reproduce it, I used a 3 Ghz 
machine with 3 GB of RAM. ipt_CRASH allocates and frees memory for every 
packet, a packet generator like "sendip" might help:
http://www.earth.li/projectpurple/progs/sendip.html

The machine dies after 15-30 minutes or as soon as I try to reboot it.

Please CC: answers, I'm only on netfilter-devel.

Thanks in advance,
Thomas

Attachment: crashit.sh
Description: application/shellscript

/* Shared library add-on to iptables to add CRASH support.
   Author: Intra2net AG <[email protected]>
   Licensed under the GPL v2.
*/

#include <stdio.h>
#include <netdb.h>
#include <string.h>
#include <stdlib.h>
#include <syslog.h>
#include <getopt.h>
#include <iptables.h>
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter_ipv4/ipt_CRASH.h>

static struct option opts[] = {
    { .name = 0 }
};

/* Function which prints out usage message. */
static void help(void)
{
    printf(
"CRASH v%s options:\n", IPTABLES_VERSION);
}

/* Initialize the target. */
static void
init(struct ipt_entry_target *t, unsigned int *nfcache)
{
    /* Can't cache this */
    *nfcache |= NFC_UNKNOWN;
}

/* Function which parses command options; returns true if it
   ate an option */
static int
parse(int c, char **argv, int invert, unsigned int *flags,
      const struct ipt_entry *entry,
      struct ipt_entry_target **target)
{
    return 0;
}

/* Final check; nothing. */
static void final_check(unsigned int flags)
{
}

static void print_it(const struct ipt_ip *ip,
                     const struct ipt_entry_target *target, char do_prefix)
{
    if (!do_prefix)
        printf("CRASH ");
}

/* Prints out the targinfo. */
static void
print(const struct ipt_ip *ip,
      const struct ipt_entry_target *target,
      int numeric)
{
    print_it (ip, target, 0);
}

/* Saves the union ipt_targinfo in parsable form to stdout. */
static void
save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
{
    print_it(ip, target, 1);
}

static
struct iptables_target crash
= {
    .next          = NULL,
    .name          = "CRASH",
    .version       = IPTABLES_VERSION,
    .size          = IPT_ALIGN(sizeof(struct ipt_crash_info)),
    .userspacesize = IPT_ALIGN(sizeof(struct ipt_crash_info)),
    .help          = &help,
    .init          = &init,
    .parse         = &parse,
    .final_check   = &final_check,
    .print         = &print,
    .save          = &save,
    .extra_opts    = opts
};

void _init(void)
{
    register_target(&crash);
}
/***************************************************************************
 *   Copyright (C) 2007 by Intra2net AG                                    *
 *   [email protected]                                              *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License                  *
 *   version 2 as published by the Free Software Foundation;               *
 *                                                                         *
 ***************************************************************************/

#include <linux/module.h>
#include <linux/version.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <net/icmp.h>
#include <net/udp.h>
#include <net/tcp.h>
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter_ipv4/ipt_CRASH.h>
#include <asm/semaphore.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/spinlock.h>
#include <asm/uaccess.h>

/* Spinlock used during memory allocation */
static DEFINE_SPINLOCK(ipt_crash_lock);

static unsigned int ipt_crash_target(struct sk_buff **pskb,
                                   const struct net_device *in,
                                   const struct net_device *out,
                                   unsigned int hooknum,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
                                   const struct xt_target *target,
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
                                   const void *targinfo)
#else
                                   const void *targinfo,
                                   void *userinfo)
#endif
{
    char *data;

    spin_lock_bh(&ipt_crash_lock);

    if ((data = (char *)get_zeroed_page(GFP_ATOMIC)) == NULL) {
        printk("ipt_CRASH: Out of memory!\n");
    } else {
        // Fill memory with pattern
        int i, j = 123;
        for (i = 0; i < PAGE_SIZE; i++) {
            data[i] = j;
            j++;
        }

        free_page((unsigned long)data);
    }

    spin_unlock_bh(&ipt_crash_lock);

    return IPT_CONTINUE;
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
static struct xt_target xt_crash_reg = {
#else
static struct ipt_target ipt_crash_reg = {
#endif
    .name = "CRASH",
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
    .family = AF_INET,
#endif
    .target = ipt_crash_target,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
    .targetsize = sizeof(struct ipt_crash_info),
#endif
    .me = THIS_MODULE
};

static int __init init(void)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
    if (xt_register_target(&xt_crash_reg))
#else
    if (ipt_register_target(&ipt_crash_reg))
#endif
        return -EINVAL;

    return 0;
}

static void __exit fini(void)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
    xt_unregister_target(&xt_crash_reg);
#else
    ipt_unregister_target(&ipt_crash_reg);
#endif
}

module_init(init);
module_exit(fini);
MODULE_LICENSE("GPL");
/***************************************************************************
 *   Copyright (C) 2004-2005 by Intra2net AG                               *
 *   [email protected]                                              *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License                  *
 *   version 2 as published by the Free Software Foundation;               *
 *                                                                         *
 ***************************************************************************/

#ifndef _IPT_CRASH_H
#define _IPT_CRASH_H

/* Structure for the userspace part of ipt_CRASH */
struct ipt_crash_info {
};

#endif /*_IPT_CRASH_H*/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux