eCryptfs: use page_alloc not kmalloc to get a page of memory
authorEric Sandeen <sandeen@redhat.com>
Mon, 28 Jul 2008 22:46:39 +0000 (15:46 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 28 Jul 2008 23:30:21 +0000 (16:30 -0700)
With SLUB debugging turned on in 2.6.26, I was getting memory corruption
when testing eCryptfs.  The root cause turned out to be that eCryptfs was
doing kmalloc(PAGE_CACHE_SIZE); virt_to_page() and treating that as a nice
page-aligned chunk of memory.  But at least with SLUB debugging on, this
is not always true, and the page we get from virt_to_page does not
necessarily match the PAGE_CACHE_SIZE worth of memory we got from kmalloc.

My simple testcase was 2 loops doing "rm -f fileX; cp /tmp/fileX ." for 2
different multi-megabyte files.  With this change I no longer see the
corruption.

Signed-off-by: Eric Sandeen <sandeen@redhat.com>
Acked-by: Michael Halcrow <mhalcrow@us.ibm.com>
Acked-by: Rik van Riel <riel@redhat.com>
Cc: <stable@kernel.org> [2.6.25.x, 2.6.26.x]
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
fs/ecryptfs/crypto.c

index 7b99917..06db79d 100644 (file)
@@ -475,8 +475,8 @@ int ecryptfs_encrypt_page(struct page *page)
 {
        struct inode *ecryptfs_inode;
        struct ecryptfs_crypt_stat *crypt_stat;
-       char *enc_extent_virt = NULL;
-       struct page *enc_extent_page;
+       char *enc_extent_virt;
+       struct page *enc_extent_page = NULL;
        loff_t extent_offset;
        int rc = 0;
 
@@ -492,14 +492,14 @@ int ecryptfs_encrypt_page(struct page *page)
                               page->index);
                goto out;
        }
-       enc_extent_virt = kmalloc(PAGE_CACHE_SIZE, GFP_USER);
-       if (!enc_extent_virt) {
+       enc_extent_page = alloc_page(GFP_USER);
+       if (!enc_extent_page) {
                rc = -ENOMEM;
                ecryptfs_printk(KERN_ERR, "Error allocating memory for "
                                "encrypted extent\n");
                goto out;
        }
-       enc_extent_page = virt_to_page(enc_extent_virt);
+       enc_extent_virt = kmap(enc_extent_page);
        for (extent_offset = 0;
             extent_offset < (PAGE_CACHE_SIZE / crypt_stat->extent_size);
             extent_offset++) {
@@ -527,7 +527,10 @@ int ecryptfs_encrypt_page(struct page *page)
                }
        }
 out:
-       kfree(enc_extent_virt);
+       if (enc_extent_page) {
+               kunmap(enc_extent_page);
+               __free_page(enc_extent_page);
+       }
        return rc;
 }
 
@@ -609,8 +612,8 @@ int ecryptfs_decrypt_page(struct page *page)
 {
        struct inode *ecryptfs_inode;
        struct ecryptfs_crypt_stat *crypt_stat;
-       char *enc_extent_virt = NULL;
-       struct page *enc_extent_page;
+       char *enc_extent_virt;
+       struct page *enc_extent_page = NULL;
        unsigned long extent_offset;
        int rc = 0;
 
@@ -627,14 +630,14 @@ int ecryptfs_decrypt_page(struct page *page)
                               page->index);
                goto out;
        }
-       enc_extent_virt = kmalloc(PAGE_CACHE_SIZE, GFP_USER);
-       if (!enc_extent_virt) {
+       enc_extent_page = alloc_page(GFP_USER);
+       if (!enc_extent_page) {
                rc = -ENOMEM;
                ecryptfs_printk(KERN_ERR, "Error allocating memory for "
                                "encrypted extent\n");
                goto out;
        }
-       enc_extent_page = virt_to_page(enc_extent_virt);
+       enc_extent_virt = kmap(enc_extent_page);
        for (extent_offset = 0;
             extent_offset < (PAGE_CACHE_SIZE / crypt_stat->extent_size);
             extent_offset++) {
@@ -662,7 +665,10 @@ int ecryptfs_decrypt_page(struct page *page)
                }
        }
 out:
-       kfree(enc_extent_virt);
+       if (enc_extent_page) {
+               kunmap(enc_extent_page);
+               __free_page(enc_extent_page);
+       }
        return rc;
 }