pmem: Fix a possible NULL pointer dereference

Fix a possible Null pointer dereference when allocating the
garbage page and fixed potential leaks caused in the error path.
Fixed potential leaks in pmem_remove.

Change-Id: I7d4fa0798553691ed80e5024b19ba363249576b3
CRs-Fixed: 313700
Signed-off-by: Vipul Gandhi <vgandhi@codeaurora.org>
diff --git a/drivers/misc/pmem.c b/drivers/misc/pmem.c
index 7ee7c11..f0d523e 100644
--- a/drivers/misc/pmem.c
+++ b/drivers/misc/pmem.c
@@ -1,7 +1,7 @@
 /* drivers/android/pmem.c
  *
  * Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -2594,6 +2594,7 @@
 {
 	int i, index = 0, id;
 	struct vm_struct *pmem_vma = NULL;
+	struct page *page;
 
 	if (id_count >= PMEM_MAX_DEVICES) {
 		pr_alert("pmem: %s: unable to register driver(%s) - no more "
@@ -2794,6 +2795,11 @@
 
 	pmem[id].base = allocate_contiguous_memory_nomap(pmem[id].size,
 		pmem[id].memory_type, PAGE_SIZE);
+	if (!pmem[id].base) {
+		pr_err("pmem: Cannot allocate from reserved memory for %s\n",
+		 pdata->name);
+		goto err_misc_deregister;
+	}
 
 	pr_info("allocating %lu bytes at %p (%lx physical) for %s\n",
 		pmem[id].size, pmem[id].vbase, pmem[id].base, pmem[id].name);
@@ -2810,7 +2816,7 @@
 			if (!pmem_vma) {
 				pr_err("pmem: Failed to allocate virtual space for "
 					"%s\n", pdata->name);
-				goto out_put_kobj;
+				goto err_free;
 			}
 			pr_err("pmem: Reserving virtual address range %lx - %lx for"
 				" %s\n", (unsigned long) pmem_vma->addr,
@@ -2821,7 +2827,12 @@
 	} else
 		pmem[id].area = NULL;
 
-	pmem[id].garbage_pfn = page_to_pfn(alloc_page(GFP_KERNEL));
+	page = alloc_page(GFP_KERNEL);
+	if (!page) {
+		pr_err("pmem: Failed to allocate page for %s\n", pdata->name);
+		goto cleanup_vm;
+	}
+	pmem[id].garbage_pfn = page_to_pfn(page);
 	atomic_set(&pmem[id].allocation_cnt, 0);
 
 	if (pdata->setup_region)
@@ -2835,6 +2846,12 @@
 
 	return 0;
 
+cleanup_vm:
+	remove_vm_area(pmem_vma);
+err_free:
+	free_contiguous_memory_by_paddr(pmem[id].base);
+err_misc_deregister:
+	misc_deregister(&pmem[id].dev);
 err_cant_register_device:
 out_put_kobj:
 	kobject_put(&pmem[id].kobj);
@@ -2872,6 +2889,19 @@
 	int id = pdev->id;
 	__free_page(pfn_to_page(pmem[id].garbage_pfn));
 	pm_runtime_disable(&pdev->dev);
+	if (pmem[id].vbase)
+		iounmap(pmem[id].vbase);
+	if (pmem[id].map_on_demand && !pmem[id].reusable && pmem[id].area)
+		free_vm_area(pmem[id].area);
+	if (pmem[id].base)
+		free_contiguous_memory_by_paddr(pmem[id].base);
+	kobject_put(&pmem[id].kobj);
+	if (pmem[id].allocator_type == PMEM_ALLOCATORTYPE_BUDDYBESTFIT)
+		kfree(pmem[id].allocator.buddy_bestfit.buddy_bitmap);
+	else if (pmem[id].allocator_type == PMEM_ALLOCATORTYPE_BITMAP) {
+		kfree(pmem[id].allocator.bitmap.bitmap);
+		kfree(pmem[id].allocator.bitmap.bitm_alloc);
+	}
 	misc_deregister(&pmem[id].dev);
 	return 0;
 }