[kernel] r16434 - in dists/sid/linux-2.6/debian: . patches/bugfix/all patches/series

Dann Frazier dannf at alioth.debian.org
Thu Oct 14 04:53:43 UTC 2010


Author: dannf
Date: Thu Oct 14 04:53:39 2010
New Revision: 16434

Log:
GFS2: Fix writing to non-page aligned gfs2_quota structures (CVE-2010-1436)

Added:
   dists/sid/linux-2.6/debian/patches/bugfix/all/gfs2-BUG-in-gfs2_adjust_quota.patch
   dists/sid/linux-2.6/debian/patches/bugfix/all/gfs2-clean-up-gfs_adjust_quota-and_do_glock.patch
   dists/sid/linux-2.6/debian/patches/bugfix/all/gfs2-fix-writing-to-non-page-aligned-gfs2_quota-structures.patch
Modified:
   dists/sid/linux-2.6/debian/changelog
   dists/sid/linux-2.6/debian/patches/series/25

Modified: dists/sid/linux-2.6/debian/changelog
==============================================================================
--- dists/sid/linux-2.6/debian/changelog	Wed Oct 13 15:53:58 2010	(r16433)
+++ dists/sid/linux-2.6/debian/changelog	Thu Oct 14 04:53:39 2010	(r16434)
@@ -32,6 +32,7 @@
   [ dann frazier ]
   * drm/i915: Sanity check pread/pwrite (CVE-2010-2962)
   * drm/i915: Rephrase pwrite bounds checking to avoid any potential overflow
+  * GFS2: Fix writing to non-page aligned gfs2_quota structures (CVE-2010-1436)
 
  -- Ben Hutchings <ben at decadent.org.uk>  Thu, 30 Sep 2010 12:28:58 +0100
 

Added: dists/sid/linux-2.6/debian/patches/bugfix/all/gfs2-BUG-in-gfs2_adjust_quota.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux-2.6/debian/patches/bugfix/all/gfs2-BUG-in-gfs2_adjust_quota.patch	Thu Oct 14 04:53:39 2010	(r16434)
@@ -0,0 +1,42 @@
+[Adjusted to apply to Debian's 2.6.32 by dann frazier <dannf at debian.org>]
+
+commit 8b4216018bdbfbb1b76150d202b15ee68c38e991
+Author: Abhijith Das <adas at redhat.com>
+Date:   Sun Jul 4 01:33:24 2010 -0400
+
+    GFS2: BUG in gfs2_adjust_quota
+    
+    HighMem pages on i686 do not get mapped to the buffer_heads and this was
+    causing a NULL pointer dereference when we were trying to memset page buffers
+    to zero.
+    We now use zero_user() that kmaps the page and directly manipulates page data.
+    This patch also fixes a boundary condition that was incorrect.
+    
+    Signed-off-by: Abhi Das <adas at redhat.com>
+    Signed-off-by: Steven Whitehouse <swhiteho at redhat.com>
+
+diff -urpN a/fs/gfs2/quota.c b/fs/gfs2/quota.c
+--- a/fs/gfs2/quota.c	2010-10-13 22:27:13.000000000 -0600
++++ b/fs/gfs2/quota.c	2010-10-13 22:28:03.000000000 -0600
+@@ -679,10 +679,8 @@ get_a_page:
+ 		if (!buffer_mapped(bh))
+ 			goto unlock_out;
+ 		/* If it's a newly allocated disk block for quota, zero it */
+-		if (buffer_new(bh)) {
+-			memset(bh->b_data, 0, bh->b_size);
+-			set_buffer_uptodate(bh);
+-		}
++		if (buffer_new(bh))
++			zero_user(page, pos - blocksize, bh->b_size);
+ 	}
+ 
+ 	if (PageUptodate(page))
+@@ -708,7 +706,7 @@ get_a_page:
+ 
+ 	/* If quota straddles page boundary, we need to update the rest of the
+ 	 * quota at the beginning of the next page */
+-	if (offset != 0) { /* first page, offset is closer to PAGE_CACHE_SIZE */
++	if ((offset + sizeof(struct gfs2_quota)) > PAGE_CACHE_SIZE) {
+ 		ptr = ptr + nbytes;
+ 		nbytes = sizeof(struct gfs2_quota) - nbytes;
+ 		offset = 0;

Added: dists/sid/linux-2.6/debian/patches/bugfix/all/gfs2-clean-up-gfs_adjust_quota-and_do_glock.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux-2.6/debian/patches/bugfix/all/gfs2-clean-up-gfs_adjust_quota-and_do_glock.patch	Thu Oct 14 04:53:39 2010	(r16434)
@@ -0,0 +1,189 @@
+commit 1e72c0f7c40e665d2ed40014750fdd2fa9968bcf
+Author: Steven Whitehouse <swhiteho at redhat.com>
+Date:   Tue Sep 15 20:42:56 2009 +0100
+
+    GFS2: Clean up gfs2_adjust_quota() and do_glock()
+    
+    Both of these functions contained confusing and in one case
+    duplicate code. This patch adds a new check in do_glock()
+    so that we report -ENOENT if we are asked to sync a quota
+    entry which doesn't exist. Due to the previous patch this is
+    now reported correctly to userspace.
+    
+    Also there are a few new comments, and I hope that the code
+    is easier to understand now.
+    
+    Signed-off-by: Steven Whitehouse <swhiteho at redhat.com>
+
+diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c
+index db124af..33e369f 100644
+--- a/fs/gfs2/quota.c
++++ b/fs/gfs2/quota.c
+@@ -15,7 +15,7 @@
+  * fuzziness in the current usage value of IDs that are being used on different
+  * nodes in the cluster simultaneously.  So, it is possible for a user on
+  * multiple nodes to overrun their quota, but that overrun is controlable.
+- * Since quota tags are part of transactions, there is no need to a quota check
++ * Since quota tags are part of transactions, there is no need for a quota check
+  * program to be run on node crashes or anything like that.
+  *
+  * There are couple of knobs that let the administrator manage the quota
+@@ -66,13 +66,6 @@
+ #define QUOTA_USER 1
+ #define QUOTA_GROUP 0
+ 
+-struct gfs2_quota_host {
+-	u64 qu_limit;
+-	u64 qu_warn;
+-	s64 qu_value;
+-	u32 qu_ll_next;
+-};
+-
+ struct gfs2_quota_change_host {
+ 	u64 qc_change;
+ 	u32 qc_flags; /* GFS2_QCF_... */
+@@ -618,33 +611,19 @@ static void do_qc(struct gfs2_quota_data *qd, s64 change)
+ 	mutex_unlock(&sdp->sd_quota_mutex);
+ }
+ 
+-static void gfs2_quota_in(struct gfs2_quota_host *qu, const void *buf)
+-{
+-	const struct gfs2_quota *str = buf;
+-
+-	qu->qu_limit = be64_to_cpu(str->qu_limit);
+-	qu->qu_warn = be64_to_cpu(str->qu_warn);
+-	qu->qu_value = be64_to_cpu(str->qu_value);
+-	qu->qu_ll_next = be32_to_cpu(str->qu_ll_next);
+-}
+-
+-static void gfs2_quota_out(const struct gfs2_quota_host *qu, void *buf)
+-{
+-	struct gfs2_quota *str = buf;
+-
+-	str->qu_limit = cpu_to_be64(qu->qu_limit);
+-	str->qu_warn = cpu_to_be64(qu->qu_warn);
+-	str->qu_value = cpu_to_be64(qu->qu_value);
+-	str->qu_ll_next = cpu_to_be32(qu->qu_ll_next);
+-	memset(&str->qu_reserved, 0, sizeof(str->qu_reserved));
+-}
+-
+ /**
+- * gfs2_adjust_quota
++ * gfs2_adjust_quota - adjust record of current block usage
++ * @ip: The quota inode
++ * @loc: Offset of the entry in the quota file
++ * @change: The amount of change to record
++ * @qd: The quota data
+  *
+  * This function was mostly borrowed from gfs2_block_truncate_page which was
+  * in turn mostly borrowed from ext3
++ *
++ * Returns: 0 or -ve on error
+  */
++
+ static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc,
+ 			     s64 change, struct gfs2_quota_data *qd)
+ {
+@@ -656,8 +635,7 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc,
+ 	struct buffer_head *bh;
+ 	struct page *page;
+ 	void *kaddr;
+-	char *ptr;
+-	struct gfs2_quota_host qp;
++	struct gfs2_quota *qp;
+ 	s64 value;
+ 	int err = -EIO;
+ 
+@@ -701,18 +679,13 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc,
+ 	gfs2_trans_add_bh(ip->i_gl, bh, 0);
+ 
+ 	kaddr = kmap_atomic(page, KM_USER0);
+-	ptr = kaddr + offset;
+-	gfs2_quota_in(&qp, ptr);
+-	qp.qu_value += change;
+-	value = qp.qu_value;
+-	gfs2_quota_out(&qp, ptr);
++	qp = kaddr + offset;
++	value = (s64)be64_to_cpu(qp->qu_value) + change;
++	qp->qu_value = cpu_to_be64(value);
++	qd->qd_qb.qb_value = qp->qu_value;
+ 	flush_dcache_page(page);
+ 	kunmap_atomic(kaddr, KM_USER0);
+ 	err = 0;
+-	qd->qd_qb.qb_magic = cpu_to_be32(GFS2_MAGIC);
+-	qd->qd_qb.qb_value = cpu_to_be64(value);
+-	((struct gfs2_quota_lvb*)(qd->qd_gl->gl_lvb))->qb_magic = cpu_to_be32(GFS2_MAGIC);
+-	((struct gfs2_quota_lvb*)(qd->qd_gl->gl_lvb))->qb_value = cpu_to_be64(value);
+ unlock:
+ 	unlock_page(page);
+ 	page_cache_release(page);
+@@ -741,8 +714,7 @@ static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda)
+ 
+ 	sort(qda, num_qd, sizeof(struct gfs2_quota_data *), sort_qd, NULL);
+ 	for (qx = 0; qx < num_qd; qx++) {
+-		error = gfs2_glock_nq_init(qda[qx]->qd_gl,
+-					   LM_ST_EXCLUSIVE,
++		error = gfs2_glock_nq_init(qda[qx]->qd_gl, LM_ST_EXCLUSIVE,
+ 					   GL_NOCACHE, &ghs[qx]);
+ 		if (error)
+ 			goto out;
+@@ -797,8 +769,7 @@ static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda)
+ 		qd = qda[x];
+ 		offset = qd2offset(qd);
+ 		error = gfs2_adjust_quota(ip, offset, qd->qd_change_sync,
+-					  (struct gfs2_quota_data *)
+-					  qd);
++					  (struct gfs2_quota_data *)qd);
+ 		if (error)
+ 			goto out_end_trans;
+ 
+@@ -829,8 +800,7 @@ static int do_glock(struct gfs2_quota_data *qd, int force_refresh,
+ 	struct gfs2_sbd *sdp = qd->qd_gl->gl_sbd;
+ 	struct gfs2_inode *ip = GFS2_I(sdp->sd_quota_inode);
+ 	struct gfs2_holder i_gh;
+-	struct gfs2_quota_host q;
+-	char buf[sizeof(struct gfs2_quota)];
++	struct gfs2_quota q;
+ 	int error;
+ 	struct gfs2_quota_lvb *qlvb;
+ 
+@@ -853,22 +823,23 @@ restart:
+ 		if (error)
+ 			goto fail;
+ 
+-		memset(buf, 0, sizeof(struct gfs2_quota));
++		memset(&q, 0, sizeof(struct gfs2_quota));
+ 		pos = qd2offset(qd);
+-		error = gfs2_internal_read(ip, NULL, buf, &pos,
+-					   sizeof(struct gfs2_quota));
++		error = gfs2_internal_read(ip, NULL, (char *)&q, &pos, sizeof(q));
+ 		if (error < 0)
+ 			goto fail_gunlock;
+-
++		if ((error < sizeof(q)) && force_refresh) {
++			error = -ENOENT;
++			goto fail_gunlock;
++		}
+ 		gfs2_glock_dq_uninit(&i_gh);
+ 
+-		gfs2_quota_in(&q, buf);
+ 		qlvb = (struct gfs2_quota_lvb *)qd->qd_gl->gl_lvb;
+ 		qlvb->qb_magic = cpu_to_be32(GFS2_MAGIC);
+ 		qlvb->__pad = 0;
+-		qlvb->qb_limit = cpu_to_be64(q.qu_limit);
+-		qlvb->qb_warn = cpu_to_be64(q.qu_warn);
+-		qlvb->qb_value = cpu_to_be64(q.qu_value);
++		qlvb->qb_limit = q.qu_limit;
++		qlvb->qb_warn = q.qu_warn;
++		qlvb->qb_value = q.qu_value;
+ 		qd->qd_qb = *qlvb;
+ 
+ 		gfs2_glock_dq_uninit(q_gh);
+@@ -1126,7 +1097,6 @@ int gfs2_quota_refresh(struct gfs2_sbd *sdp, int user, u32 id)
+ 		gfs2_glock_dq_uninit(&q_gh);
+ 
+ 	qd_put(qd);
+-
+ 	return error;
+ }
+ 

Added: dists/sid/linux-2.6/debian/patches/bugfix/all/gfs2-fix-writing-to-non-page-aligned-gfs2_quota-structures.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/sid/linux-2.6/debian/patches/bugfix/all/gfs2-fix-writing-to-non-page-aligned-gfs2_quota-structures.patch	Thu Oct 14 04:53:39 2010	(r16434)
@@ -0,0 +1,126 @@
+[Backported to Debian's 2.6.32 by dann frazier <dannf at debian.org>]
+
+commit 7e619bc3e6252dc746f64ac3b486e784822e9533
+Author: Abhijith Das <adas at redhat.com>
+Date:   Fri May 7 17:50:18 2010 -0400
+
+    GFS2: Fix writing to non-page aligned gfs2_quota structures
+    
+    This is the upstream fix for this bug. This patch differs
+    from the RHEL5 fix (Red Hat bz #555754) which simply writes to the 8-byte
+    value field of the quota. In upstream quota code, we're
+    required to write the entire quota (88 bytes) which can be split
+    across a page boundary. We check for such quotas, and read/write
+    the two parts from/to the corresponding pages holding these parts.
+    
+    With this patch, I don't see the bug anymore using the reproducer
+    in Red Hat bz 555754. I successfully ran a couple of simple tests/mounts/
+    umounts and it doesn't seem like this patch breaks anything else.
+    
+    Signed-off-by: Abhi Das <adas at redhat.com>
+    Signed-off-by: Steven Whitehouse <swhiteho at redhat.com>
+
+diff -urpN a/fs/gfs2/quota.c b/fs/gfs2/quota.c
+--- a/fs/gfs2/quota.c	2010-10-13 17:49:03.000000000 -0600
++++ b/fs/gfs2/quota.c	2010-10-13 17:57:32.000000000 -0600
+@@ -633,14 +633,29 @@ static int gfs2_adjust_quota(struct gfs2
+ 	unsigned blocksize, iblock, pos;
+ 	struct buffer_head *bh;
+ 	struct page *page;
+-	void *kaddr;
+-	struct gfs2_quota *qp;
+-	s64 value;
+-	int err = -EIO;
++	void *kaddr, *ptr;
++	struct gfs2_quota q, *qp;
++	int err, nbytes;
+ 
+ 	if (gfs2_is_stuffed(ip))
+ 		gfs2_unstuff_dinode(ip, NULL);
+-	
++
++	memset(&q, 0, sizeof(struct gfs2_quota));
++	err = gfs2_internal_read(ip, NULL, (char *)&q, &loc, sizeof(q));
++	if (err < 0)
++		return err;
++
++	err = -EIO;
++	qp = &q;
++	qp->qu_value = be64_to_cpu(qp->qu_value);
++	qp->qu_value += change;
++	qp->qu_value = cpu_to_be64(qp->qu_value);
++	qd->qd_qb.qb_value = qp->qu_value;
++
++	/* Write the quota into the quota file on disk */
++	ptr = qp;
++	nbytes = sizeof(struct gfs2_quota);
++get_a_page:
+ 	page = grab_cache_page(mapping, index);
+ 	if (!page)
+ 		return -ENOMEM;
+@@ -662,7 +677,12 @@ static int gfs2_adjust_quota(struct gfs2
+ 	if (!buffer_mapped(bh)) {
+ 		gfs2_block_map(inode, iblock, bh, 1);
+ 		if (!buffer_mapped(bh))
+-			goto unlock;
++			goto unlock_out;
++		/* If it's a newly allocated disk block for quota, zero it */
++		if (buffer_new(bh)) {
++			memset(bh->b_data, 0, bh->b_size);
++			set_buffer_uptodate(bh);
++		}
+ 	}
+ 
+ 	if (PageUptodate(page))
+@@ -672,20 +692,32 @@ static int gfs2_adjust_quota(struct gfs2
+ 		ll_rw_block(READ_META, 1, &bh);
+ 		wait_on_buffer(bh);
+ 		if (!buffer_uptodate(bh))
+-			goto unlock;
++			goto unlock_out;
+ 	}
+ 
+ 	gfs2_trans_add_bh(ip->i_gl, bh, 0);
+ 
+ 	kaddr = kmap_atomic(page, KM_USER0);
+-	qp = kaddr + offset;
+-	value = (s64)be64_to_cpu(qp->qu_value) + change;
+-	qp->qu_value = cpu_to_be64(value);
+-	qd->qd_qb.qb_value = qp->qu_value;
++	if (offset + sizeof(struct gfs2_quota) > PAGE_CACHE_SIZE)
++		nbytes = PAGE_CACHE_SIZE - offset;
++	memcpy(kaddr + offset, ptr, nbytes);
+ 	flush_dcache_page(page);
+ 	kunmap_atomic(kaddr, KM_USER0);
++	unlock_page(page);
++	page_cache_release(page);
++
++	/* If quota straddles page boundary, we need to update the rest of the
++	 * quota at the beginning of the next page */
++	if (offset != 0) { /* first page, offset is closer to PAGE_CACHE_SIZE */
++		ptr = ptr + nbytes;
++		nbytes = sizeof(struct gfs2_quota) - nbytes;
++		offset = 0;
++		index++;
++		goto get_a_page;
++	}
+ 	err = 0;
+-unlock:
++	return err;
++unlock_out:
+ 	unlock_page(page);
+ 	page_cache_release(page);
+ 	return err;
+@@ -748,8 +780,10 @@ static int do_sync(unsigned int num_qd,
+ 	 * rgrp since it won't be allocated during the transaction
+ 	 */
+ 	al->al_requested = 1;
+-	/* +1 in the end for block requested above for unstuffing */
+-	blocks = num_qd * data_blocks + RES_DINODE + num_qd + 1;
++	/* +3 in the end for unstuffing block, inode size update block
++	 * and another block in case quota straddles page boundary and 
++	 * two blocks need to be updated instead of 1 */
++	blocks = num_qd * data_blocks + RES_DINODE + num_qd + 3;
+ 
+ 	if (nalloc)
+ 		al->al_requested += nalloc * (data_blocks + ind_blocks);		

Modified: dists/sid/linux-2.6/debian/patches/series/25
==============================================================================
--- dists/sid/linux-2.6/debian/patches/series/25	Wed Oct 13 15:53:58 2010	(r16433)
+++ dists/sid/linux-2.6/debian/patches/series/25	Thu Oct 14 04:53:39 2010	(r16434)
@@ -18,3 +18,6 @@
 + features/x86/toshiba_acpi-Add-full-hotkey-support.patch
 + bugfix/all/drm-i915-sanity-check-pread+pwrite.patch
 + bugfix/all/drm-i915-rephrase-pread+pwrite-bounds-checking-to-avoid-potential-overflow.patch
++ bugfix/all/gfs2-clean-up-gfs_adjust_quota-and_do_glock.patch
++ bugfix/all/gfs2-fix-writing-to-non-page-aligned-gfs2_quota-structures.patch
++ bugfix/all/gfs2-BUG-in-gfs2_adjust_quota.patch



More information about the Kernel-svn-changes mailing list