/* brk_fix
 * Verions: 0.01
 *
 * This Module is a proof of concept fix for the do_brk() vulnerability,
 * it protects only the entrance to the system through the syscall
 * brk(). There is another possibility to call do_brk() indirectly
 * through the elf loader - so this module is no full protection for
 * your system! 
 * For full protection see the kernel patch linux-2.4.21-do_brk.patch
 *
 *  Written by Torsten Hoefler <htor@unixer.de>
 *  (c) Copyright 2003 Torsten Hoefler
 *             All rights reserved.
 *
 * This Module is distributed under GPL without ANY warranty.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 *  02111-1307  USA
 *
 * sample usage: 
 *   # gcc -O2 -DMODULE -D__KERNEL__  -isystem /lib/modules/`uname
 *       -r`/build/include   -c -o do_brk.o do_brk.c
 *	# insmod ./do_brk.o
 *
 */

#define MODULE_NAME "do_brk_patch"
#define MODULE_VERSION "0.01"

/* Pagesize includes */
#include <asm/page.h>


/* Standard in kernel modules */
#include <linux/kernel.h>   
#include <linux/module.h>  
#include <linux/errno.h>
#include <linux/version.h> 
#include <sys/syscall.h> // syscalls 
#include <linux/sched.h> // current process :)



#include <sys/syscall.h>  /* The list of system calls */

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,9)                                 
#ifdef MODULE_LICENSE                                                           
MODULE_LICENSE("GPL");
#endif
#endif

extern void *sys_call_table[];

/* original function */
asmlinkage unsigned long (*original_call)(unsigned long brk);

/* my function - execute before original to check process mem size
 * borders */
asmlinkage unsigned long my_sys_brk(unsigned long brk) 
{
        unsigned long newbrk, oldbrk, len;
        struct mm_struct *mm = current->mm;

        if (! brk < mm->end_code) {
          newbrk = PAGE_ALIGN(brk);
          oldbrk = PAGE_ALIGN(mm->brk);
		  }

		  len = newbrk-oldbrk;
		  len = PAGE_ALIGN(len);
        
        if ((newbrk > oldbrk) && (len) && ( (oldbrk + len) > TASK_SIZE || (oldbrk + len) < oldbrk) ){
                printk("TASK_SIZE=%u, oldbrk=%u, newbrk=%u, len=%u\n", TASK_SIZE, oldbrk, newbrk, len);
                printk("htor-module: somebody tried a do_brk exploit!!!\n");
                return -EINVAL;
        }

     return original_call(brk);
}

int init_module()
{
  original_call = sys_call_table[__NR_brk];
  sys_call_table[__NR_brk] = my_sys_brk;

  return 0;
}


void cleanup_module()
{
  if (sys_call_table[__NR_brk] != my_sys_brk) {
    printk("ATTENTION: the system call table was changed.\n");
    printk("ATTENTION: it will be restored now to the old value.\n");
  }

  sys_call_table[__NR_brk] = original_call;
}  
