From: Jerome Marchand <jmarchan@redhat.com> Date: Thu, 23 Jun 2011 13:04:06 -0400 Subject: [char] agp: fix arbitrary kernel memory writes Message-id: <4E0339C6.2060902@redhat.com> Patchwork-id: 36993 O-Subject: [RHEL5.7 PATCH] CVE-2011-1745, CVE-2011-2022: agp: fix arbitrary kernel memory writes Bugzilla: 699006 CVE: CVE-2011-1745 CVE-2011-2022 RH-Acked-by: Petr Matousek <pmatouse@redhat.com> RH-Acked-by: Dave Airlie <airlied@redhat.com> Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=699006 This is a backport of the following commit: commit 194b3da873fd334ef183806db751473512af29ce Author: Vasiliy Kulikov <segoon@openwall.com> Date: Thu Apr 14 20:55:16 2011 +0400 agp: fix arbitrary kernel memory writes pg_start is copied from userspace on AGPIOC_BIND and AGPIOC_UNBIND ioctl cmds of agp_ioctl() and passed to agpioc_bind_wrap(). As said in the comment, (pg_start + mem->page_count) may wrap in case of AGPIOC_BIND, and it is not checked at all in case of AGPIOC_UNBIND. As a result, user with sufficient privileges (usually "video" group) may generate either local DoS or privilege escalation. Signed-off-by: Vasiliy Kulikov <segoon@openwall.com> Signed-off-by: Dave Airlie <airlied@redhat.com> This patch fixes both CVE-2011-1745 and CVE-2011-2022. I haven't tested it yet, for lack of hardware. Regards, Jerome diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index 57af6cd..563feab 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -975,8 +975,8 @@ int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type) return -EINVAL; } - /* AK: could wrap */ - if ((pg_start + mem->page_count) > num_entries) + if (((pg_start + mem->page_count) > num_entries) || + ((pg_start + mem->page_count) < pg_start)) return -EINVAL; j = pg_start; @@ -1007,6 +1007,7 @@ int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type) { size_t i; struct agp_bridge_data *bridge; + int num_entries; bridge = mem->bridge; if (!bridge) @@ -1017,6 +1018,11 @@ int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type) return -EINVAL; } + num_entries = agp_num_entries(); + if (((pg_start + mem->page_count) > num_entries) || + ((pg_start + mem->page_count) < pg_start)) + return -EINVAL; + /* AK: bogus, should encode addresses > 4GB */ for (i = pg_start; i < (mem->page_count + pg_start); i++) { writel(bridge->scratch_page, bridge->gatt_table+i);