Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 89877e42827f16fa5f86b1df0c2860b1 > files > 2283

kernel-2.6.18-128.1.10.el5.src.rpm

From: Jerome Marchand <jmarchan@redhat.com>
Date: Thu, 31 Jul 2008 15:02:35 +0200
Subject: [security] NULL ptr dereference in __vm_enough_memory
Message-id: 4891B7EB.90900@redhat.com
O-Subject: Re: [RHEL5.3 4/3] fix NULL pointer dereference in __vm_enough_memory()
Bugzilla: 443659
RH-Acked-by: Eric Paris <eparis@redhat.com>
RH-Acked-by: Jiri Pirko <jpirko@redhat.com>
RH-Acked-by: Jiri Pirko <jpirko@redhat.com>
RH-Acked-by: Jiri Pirko <jpirko@redhat.com>

fix NULL pointer dereference in __vm_enough_memory()

Bugzilla:
https://bugzilla.redhat.com/show_bug.cgi?id=443659

Description: (from cvs commit)
The new exec code inserts an accounted vma into an mm struct which is not
current->mm. The existing memory check code has a hard coded assumption
that this does not happen as does the security code.

As the correct mm is known we pass the mm to the security method and the
helper function. A new security test is added for the case where we need
to pass the mm and the existing one is modified to pass current->mm to
avoid the need to change large amounts of code.

Upstream status:
commit: 34b4e4aa3c470ce8fa2bd78abb1741b4b58baad7

diff --git a/include/linux/mm.h b/include/linux/mm.h
index 491eee4..8fe1de1 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -999,7 +999,8 @@ static inline void vma_nonlinear_insert(struct vm_area_struct *vma,
 }
 
 /* mmap.c */
-extern int __vm_enough_memory(long pages, int cap_sys_admin);
+extern int __vm_enough_memory(struct mm_struct *mm, long pages,
+			      int cap_sys_admin);
 extern void vma_adjust(struct vm_area_struct *vma, unsigned long start,
 	unsigned long end, pgoff_t pgoff, struct vm_area_struct *insert);
 extern struct vm_area_struct *vma_merge(struct mm_struct *,
diff --git a/include/linux/security.h b/include/linux/security.h
index 98fd375..a80f8f4 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -55,6 +55,7 @@ extern int cap_task_post_setuid (uid_t old_ruid, uid_t old_euid, uid_t old_suid,
 extern void cap_task_reparent_to_init (struct task_struct *p);
 extern int cap_syslog (int type);
 extern int cap_vm_enough_memory (long pages);
+extern int cap_vm_enough_memory_mm(struct mm_struct *mm, long pages);
 
 struct msghdr;
 struct sk_buff;
@@ -1127,6 +1128,11 @@ struct request_sock;
  *	Check permissions for allocating a new virtual mapping.
  *      @pages contains the number of pages.
  *	Return 0 if permission is granted.
+ * @vm_enough_memory_mm:
+ *	Check permissions for allocating a new virtual mapping.
+ *	@mm contains the mm struct it is being added to.
+ *	@pages contains the number of pages.
+ *	Return 0 if permission is granted.
  *
  * @register_security:
  * 	allow module stacking.
@@ -1402,6 +1408,7 @@ struct security_operations {
 				unsigned long reqprot,
 				unsigned long prot, unsigned long flags,
 				unsigned long addr, unsigned long addr_only);
+	int (*vm_enough_memory_mm) (struct mm_struct *mm, long pages);
 #endif
 };
 
@@ -1480,6 +1487,11 @@ static inline int security_vm_enough_memory(long pages)
 	return security_ops->vm_enough_memory(pages);
 }
 
+static inline int security_vm_enough_memory_mm(struct mm_struct *mm, long pages)
+{
+	return security_ops->vm_enough_memory_mm(mm, pages);
+}
+
 static inline int security_bprm_alloc (struct linux_binprm *bprm)
 {
 	return security_ops->bprm_alloc_security (bprm);
@@ -2234,6 +2246,11 @@ static inline int security_vm_enough_memory(long pages)
 	return cap_vm_enough_memory(pages);
 }
 
+static inline int security_vm_enough_memory_mm(struct mm_struct *mm, long pages)
+{
+	return cap_vm_enough_memory_mm(mm, pages);
+}
+
 static inline int security_bprm_alloc (struct linux_binprm *bprm)
 {
 	return 0;
diff --git a/mm/mmap.c b/mm/mmap.c
index 264fb13..5286ab1 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -93,7 +93,7 @@ atomic_t vm_committed_space = ATOMIC_INIT(0);
  * Note this is a helper function intended to be used by LSMs which
  * wish to use this logic.
  */
-int __vm_enough_memory(long pages, int cap_sys_admin)
+int __vm_enough_memory(struct mm_struct *mm, long pages, int cap_sys_admin)
 {
 	unsigned long free, allowed;
 
@@ -166,7 +166,7 @@ int __vm_enough_memory(long pages, int cap_sys_admin)
 
 	/* Don't let a single process grow too big:
 	   leave 3% of the size of this process for other processes */
-	allowed -= current->mm->total_vm / 32;
+	allowed -= mm->total_vm / 32;
 
 	/*
 	 * cast `allowed' as a signed long because vm_committed_space
@@ -2127,7 +2127,7 @@ int insert_vm_struct(struct mm_struct * mm, struct vm_area_struct * vma)
 	if (__vma && __vma->vm_start < vma->vm_end)
 		return -ENOMEM;
 	if ((vma->vm_flags & VM_ACCOUNT) &&
-	     security_vm_enough_memory(vma_pages(vma)))
+	     security_vm_enough_memory_mm(mm, vma_pages(vma)))
 		return -ENOMEM;
 	vma_link(mm, vma, prev, rb_link, rb_parent);
 	return 0;
diff --git a/mm/nommu.c b/mm/nommu.c
index 084fb90..6fb88e5 100644
--- a/mm/nommu.c
+++ b/mm/nommu.c
@@ -1112,7 +1112,7 @@ EXPORT_SYMBOL(unmap_mapping_range);
  * Note this is a helper function intended to be used by LSMs which
  * wish to use this logic.
  */
-int __vm_enough_memory(long pages, int cap_sys_admin)
+int __vm_enough_memory(struct mm_struct *mm, long pages, int cap_sys_admin)
 {
 	unsigned long free, allowed;
 
diff --git a/security/capability.c b/security/capability.c
index b868e7e..0a64dce 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -46,6 +46,7 @@ static struct security_operations capability_ops = {
 	.syslog =                       cap_syslog,
 
 	.vm_enough_memory =             cap_vm_enough_memory,
+	.vm_enough_memory_mm =          cap_vm_enough_memory_mm,
 };
 
 /* flag to keep track of how we were registered */
diff --git a/security/commoncap.c b/security/commoncap.c
index f50fc29..e6b6736 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -316,13 +316,18 @@ int cap_syslog (int type)
 	return 0;
 }
 
-int cap_vm_enough_memory(long pages)
+int cap_vm_enough_memory_mm(struct mm_struct *mm, long pages)
 {
 	int cap_sys_admin = 0;
 
 	if (cap_capable(current, CAP_SYS_ADMIN) == 0)
 		cap_sys_admin = 1;
-	return __vm_enough_memory(pages, cap_sys_admin);
+	return __vm_enough_memory(mm, pages, cap_sys_admin);
+}
+
+int cap_vm_enough_memory(long pages)
+{
+	return cap_vm_enough_memory_mm(current->mm, pages);
 }
 
 EXPORT_SYMBOL(cap_capable);
@@ -340,6 +345,7 @@ EXPORT_SYMBOL(cap_task_post_setuid);
 EXPORT_SYMBOL(cap_task_reparent_to_init);
 EXPORT_SYMBOL(cap_syslog);
 EXPORT_SYMBOL(cap_vm_enough_memory);
+EXPORT_SYMBOL(cap_vm_enough_memory_mm);
 
 MODULE_DESCRIPTION("Standard Linux Common Capabilities Security Module");
 MODULE_LICENSE("GPL");
diff --git a/security/dummy.c b/security/dummy.c
index 962235e..966262e 100644
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -108,13 +108,18 @@ static int dummy_settime(struct timespec *ts, struct timezone *tz)
 	return 0;
 }
 
-static int dummy_vm_enough_memory(long pages)
+static int dummy_vm_enough_memory_mm(struct mm_struct *mm, long pages)
 {
 	int cap_sys_admin = 0;
 
 	if (dummy_capable(current, CAP_SYS_ADMIN) == 0)
 		cap_sys_admin = 1;
-	return __vm_enough_memory(pages, cap_sys_admin);
+	return __vm_enough_memory(mm, pages, cap_sys_admin);
+}
+
+static int dummy_vm_enough_memory(long pages)
+{
+	return dummy_vm_enough_memory_mm(current->mm, pages);
 }
 
 static int dummy_bprm_alloc_security (struct linux_binprm *bprm)
@@ -981,6 +986,7 @@ void security_fixup_ops (struct security_operations *ops)
 	set_to_dummy_if_null(ops, syslog);
 	set_to_dummy_if_null(ops, settime);
 	set_to_dummy_if_null(ops, vm_enough_memory);
+	set_to_dummy_if_null(ops, vm_enough_memory_mm);
 	set_to_dummy_if_null(ops, bprm_alloc_security);
 	set_to_dummy_if_null(ops, bprm_free_security);
 	set_to_dummy_if_null(ops, bprm_apply_creds);
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 7ede861..b30a281 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1575,7 +1575,7 @@ static int selinux_syslog(int type)
  * Do not audit the selinux permission check, as this is applied to all
  * processes that allocate mappings.
  */
-static int selinux_vm_enough_memory(long pages)
+static int selinux_vm_enough_memory_mm(struct mm_struct *mm, long pages)
 {
 	int rc, cap_sys_admin = 0;
 	struct task_security_struct *tsec = current->security;
@@ -1590,7 +1590,12 @@ static int selinux_vm_enough_memory(long pages)
 	if (rc == 0)
 		cap_sys_admin = 1;
 
-	return __vm_enough_memory(pages, cap_sys_admin);
+	return __vm_enough_memory(mm, pages, cap_sys_admin);
+}
+
+static int selinux_vm_enough_memory(long pages)
+{
+	return selinux_vm_enough_memory_mm(current->mm, pages);
 }
 
 /* binprm security operations */
@@ -4657,6 +4662,7 @@ static struct security_operations selinux_ops = {
 	.quota_on =			selinux_quota_on,
 	.syslog =			selinux_syslog,
 	.vm_enough_memory =		selinux_vm_enough_memory,
+	.vm_enough_memory_mm =		selinux_vm_enough_memory_mm,
 
 	.netlink_send =			selinux_netlink_send,
         .netlink_recv =			selinux_netlink_recv,