• Porpoise's avatar
    [PATCH] When CONFIG_BASE_SMALL=1, cascade() may enter an infinite loop · 3439dd86
    Porpoise authored
    When CONFIG_BASE_SAMLL=1, cascade() in may enter the infinite loop.
    Because of CONFIG_BASE_SMALL=1(TVR_BITS=6 and TVN_BITS=4), the list
    base->tv5 may cascade into base->tv5.  So, the kernel enters the infinite
    loop in the function cascade().
    
    I created a test module to verify this bug, and a patch to fix it.
    
    #include <linux/kernel.h>
    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/timer.h>
    #if 0
    #include <linux/kdb.h>
    #else
    #define kdb_printf printk
    #endif
    
    #define TVN_BITS (CONFIG_BASE_SMALL ? 4 : 6)
    #define TVR_BITS (CONFIG_BASE_SMALL ? 6 : 8)
    #define TVN_SIZE (1 << TVN_BITS)
    #define TVR_SIZE (1 << TVR_BITS)
    #define TVN_MASK (TVN_SIZE - 1)
    #define TVR_MASK (TVR_SIZE - 1)
    
    #define TV_SIZE(N)  (N*TVN_BITS  + TVR_BITS)
    
    struct timer_list timer0;
    struct timer_list dummy_timer1;
    struct timer_list dummy_timer2;
    
    void dummy_timer_fun(unsigned long data) {
    }
    unsigned long j=0;
    void check_timer_base(unsigned long data)
    {
            kdb_printf("check_timer_base %08x\n",jiffies);
            mod_timer(&timer0,(jiffies & (~0xFFF)) + 0x1FFF);
    }
    
    int init_module(void)
    {
            init_timer(&timer0);
            timer0.data = (unsigned long)0;
            timer0.function = check_timer_base;
            mod_timer(&timer0,jiffies+1);
    
            init_timer(&dummy_timer1);
            dummy_timer1.data = (unsigned long)0;
            dummy_timer1.function = dummy_timer_fun;
    
            init_timer(&dummy_timer2);
            dummy_timer2.data = (unsigned long)0;
            dummy_timer2.function = dummy_timer_fun;
    
            j=jiffies;
            j&=(~((1<<TV_SIZE(3))-1));
            j+=(1<<TV_SIZE(3));
            j+=(1<<TV_SIZE(4));
    
            kdb_printf("mod_timer %08x\n",j);
    
            mod_timer(&dummy_timer1, j );
            mod_timer(&dummy_timer2, j );
    
            return 0;
    }
    
    void cleanup_module()
    {
            del_timer_sync(&timer0);
            del_timer_sync(&dummy_timer1);
            del_timer_sync(&dummy_timer2);
    }
    
    (Cleanups from Oleg)
    
    [oleg@tv-sign.ru: use list_replace_init()]
    Cc: Oleg Nesterov <oleg@tv-sign.ru>
    Cc: Matt Mackall <mpm@selenic.com>
    Signed-off-by: default avatarOleg Nesterov <oleg@tv-sign.ru>
    Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
    Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
    3439dd86
timer.c 41.2 KB