diff options
author | Vasiliy Kulikov <segoon@openwall.com> | 2011-04-14 12:55:16 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2011-04-20 22:16:55 -0400 |
commit | 194b3da873fd334ef183806db751473512af29ce (patch) | |
tree | cb6ac3d05ba2e0a07c02ea1cef0e41477ea29d2b /drivers/char | |
parent | b522f02184b413955f3bc952e3776ce41edc6355 (diff) |
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>
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/agp/generic.c | 11 |
1 files changed, 8 insertions, 3 deletions
diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index 850a643ad694..b072648dc3f6 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c | |||
@@ -1095,8 +1095,8 @@ int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type) | |||
1095 | return -EINVAL; | 1095 | return -EINVAL; |
1096 | } | 1096 | } |
1097 | 1097 | ||
1098 | /* AK: could wrap */ | 1098 | if (((pg_start + mem->page_count) > num_entries) || |
1099 | if ((pg_start + mem->page_count) > num_entries) | 1099 | ((pg_start + mem->page_count) < pg_start)) |
1100 | return -EINVAL; | 1100 | return -EINVAL; |
1101 | 1101 | ||
1102 | j = pg_start; | 1102 | j = pg_start; |
@@ -1130,7 +1130,7 @@ int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type) | |||
1130 | { | 1130 | { |
1131 | size_t i; | 1131 | size_t i; |
1132 | struct agp_bridge_data *bridge; | 1132 | struct agp_bridge_data *bridge; |
1133 | int mask_type; | 1133 | int mask_type, num_entries; |
1134 | 1134 | ||
1135 | bridge = mem->bridge; | 1135 | bridge = mem->bridge; |
1136 | if (!bridge) | 1136 | if (!bridge) |
@@ -1142,6 +1142,11 @@ int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type) | |||
1142 | if (type != mem->type) | 1142 | if (type != mem->type) |
1143 | return -EINVAL; | 1143 | return -EINVAL; |
1144 | 1144 | ||
1145 | num_entries = agp_num_entries(); | ||
1146 | if (((pg_start + mem->page_count) > num_entries) || | ||
1147 | ((pg_start + mem->page_count) < pg_start)) | ||
1148 | return -EINVAL; | ||
1149 | |||
1145 | mask_type = bridge->driver->agp_type_to_mask_type(bridge, type); | 1150 | mask_type = bridge->driver->agp_type_to_mask_type(bridge, type); |
1146 | if (mask_type != 0) { | 1151 | if (mask_type != 0) { |
1147 | /* The generic routines know nothing of memory types */ | 1152 | /* The generic routines know nothing of memory types */ |