mirror of
https://github.com/adulau/aha.git
synced 2024-12-28 03:36:19 +00:00
eCryptfs: Allocate a variable number of pages for file headers
When allocating the memory used to store the eCryptfs header contents, a single, zeroed page was being allocated with get_zeroed_page(). However, the size of an eCryptfs header is either PAGE_CACHE_SIZE or ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE (8192), whichever is larger, and is stored in the file's private_data->crypt_stat->num_header_bytes_at_front field. ecryptfs_write_metadata_to_contents() was using num_header_bytes_at_front to decide how many bytes should be written to the lower filesystem for the file header. Unfortunately, at least 8K was being written from the page, despite the chance of the single, zeroed page being smaller than 8K. This resulted in random areas of kernel memory being written between the 0x1000 and 0x1FFF bytes offsets in the eCryptfs file headers if PAGE_SIZE was 4K. This patch allocates a variable number of pages, calculated with num_header_bytes_at_front, and passes the number of allocated pages along to ecryptfs_write_metadata_to_contents(). Thanks to Florian Streibelt for reporting the data leak and working with me to find the problem. 2.6.28 is the only kernel release with this vulnerability. Corresponds to CVE-2009-0787 Signed-off-by: Tyler Hicks <tyhicks@linux.vnet.ibm.com> Acked-by: Dustin Kirkland <kirkland@canonical.com> Reviewed-by: Eric Sandeen <sandeen@redhat.com> Reviewed-by: Eugene Teo <eugeneteo@kernel.sg> Cc: Greg KH <greg@kroah.com> Cc: dann frazier <dannf@dannf.org> Cc: Serge E. Hallyn <serue@us.ibm.com> Cc: Florian Streibelt <florian@f-streibelt.de> Cc: stable@kernel.org Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
18a0d89e54
commit
8faece5f90
1 changed files with 26 additions and 13 deletions
|
@ -1324,14 +1324,13 @@ static int ecryptfs_write_headers_virt(char *page_virt, size_t max,
|
|||
}
|
||||
|
||||
static int
|
||||
ecryptfs_write_metadata_to_contents(struct ecryptfs_crypt_stat *crypt_stat,
|
||||
struct dentry *ecryptfs_dentry,
|
||||
char *virt)
|
||||
ecryptfs_write_metadata_to_contents(struct dentry *ecryptfs_dentry,
|
||||
char *virt, size_t virt_len)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = ecryptfs_write_lower(ecryptfs_dentry->d_inode, virt,
|
||||
0, crypt_stat->num_header_bytes_at_front);
|
||||
0, virt_len);
|
||||
if (rc)
|
||||
printk(KERN_ERR "%s: Error attempting to write header "
|
||||
"information to lower file; rc = [%d]\n", __func__,
|
||||
|
@ -1341,7 +1340,6 @@ ecryptfs_write_metadata_to_contents(struct ecryptfs_crypt_stat *crypt_stat,
|
|||
|
||||
static int
|
||||
ecryptfs_write_metadata_to_xattr(struct dentry *ecryptfs_dentry,
|
||||
struct ecryptfs_crypt_stat *crypt_stat,
|
||||
char *page_virt, size_t size)
|
||||
{
|
||||
int rc;
|
||||
|
@ -1351,6 +1349,17 @@ ecryptfs_write_metadata_to_xattr(struct dentry *ecryptfs_dentry,
|
|||
return rc;
|
||||
}
|
||||
|
||||
static unsigned long ecryptfs_get_zeroed_pages(gfp_t gfp_mask,
|
||||
unsigned int order)
|
||||
{
|
||||
struct page *page;
|
||||
|
||||
page = alloc_pages(gfp_mask | __GFP_ZERO, order);
|
||||
if (page)
|
||||
return (unsigned long) page_address(page);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_write_metadata
|
||||
* @ecryptfs_dentry: The eCryptfs dentry
|
||||
|
@ -1367,7 +1376,9 @@ int ecryptfs_write_metadata(struct dentry *ecryptfs_dentry)
|
|||
{
|
||||
struct ecryptfs_crypt_stat *crypt_stat =
|
||||
&ecryptfs_inode_to_private(ecryptfs_dentry->d_inode)->crypt_stat;
|
||||
unsigned int order;
|
||||
char *virt;
|
||||
size_t virt_len;
|
||||
size_t size = 0;
|
||||
int rc = 0;
|
||||
|
||||
|
@ -1383,33 +1394,35 @@ int ecryptfs_write_metadata(struct dentry *ecryptfs_dentry)
|
|||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
virt_len = crypt_stat->num_header_bytes_at_front;
|
||||
order = get_order(virt_len);
|
||||
/* Released in this function */
|
||||
virt = (char *)get_zeroed_page(GFP_KERNEL);
|
||||
virt = (char *)ecryptfs_get_zeroed_pages(GFP_KERNEL, order);
|
||||
if (!virt) {
|
||||
printk(KERN_ERR "%s: Out of memory\n", __func__);
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
rc = ecryptfs_write_headers_virt(virt, PAGE_CACHE_SIZE, &size,
|
||||
crypt_stat, ecryptfs_dentry);
|
||||
rc = ecryptfs_write_headers_virt(virt, virt_len, &size, crypt_stat,
|
||||
ecryptfs_dentry);
|
||||
if (unlikely(rc)) {
|
||||
printk(KERN_ERR "%s: Error whilst writing headers; rc = [%d]\n",
|
||||
__func__, rc);
|
||||
goto out_free;
|
||||
}
|
||||
if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR)
|
||||
rc = ecryptfs_write_metadata_to_xattr(ecryptfs_dentry,
|
||||
crypt_stat, virt, size);
|
||||
rc = ecryptfs_write_metadata_to_xattr(ecryptfs_dentry, virt,
|
||||
size);
|
||||
else
|
||||
rc = ecryptfs_write_metadata_to_contents(crypt_stat,
|
||||
ecryptfs_dentry, virt);
|
||||
rc = ecryptfs_write_metadata_to_contents(ecryptfs_dentry, virt,
|
||||
virt_len);
|
||||
if (rc) {
|
||||
printk(KERN_ERR "%s: Error writing metadata out to lower file; "
|
||||
"rc = [%d]\n", __func__, rc);
|
||||
goto out_free;
|
||||
}
|
||||
out_free:
|
||||
free_page((unsigned long)virt);
|
||||
free_pages((unsigned long)virt, order);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue