The code contains an out-of-bounds write vulnerability due to insufficient bounds validation. Negative pg_start values and integer overflow in pg_start+pg_count can bypass the existing bounds check.
For example, pg_start=-1 with page_count=1 produces a sum of 0, passing the check `(pg_start + page_count) > num_entries`, but later writes to ptes[-1]. Similarly, pg_start=LONG_MAX-5 with pg_count=10 overflows, bypassing the check.
Fix by explicitly rejecting negative pg_start and detecting overflow in alpha_core_agp_insert_memory, alpha_core_agp_remove_memory, iommu_release, iommu_bind, and iommu_unbind.
Reported-by: Yuhao Jiang danisjiang@gmail.com Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Yuhao Jiang danisjiang@gmail.com --- arch/alpha/kernel/pci_iommu.c | 17 ++++++++++++++++- drivers/char/agp/alpha-agp.c | 13 ++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-)
diff --git a/arch/alpha/kernel/pci_iommu.c b/arch/alpha/kernel/pci_iommu.c index dc91de50f906..b6293dc66d45 100644 --- a/arch/alpha/kernel/pci_iommu.c +++ b/arch/alpha/kernel/pci_iommu.c @@ -859,6 +859,11 @@ iommu_release(struct pci_iommu_arena *arena, long pg_start, long pg_count)
if (!arena) return -EINVAL;
+ if (pg_start < 0 || pg_start + pg_count > (arena->size >> PAGE_SHIFT)) + return -EINVAL; + if (pg_start + pg_count < pg_start) + return -EINVAL; + ptes = arena->ptes;
/* Make sure they're all reserved first... */ @@ -879,7 +884,12 @@ iommu_bind(struct pci_iommu_arena *arena, long pg_start, long pg_count, long i, j;
if (!arena) return -EINVAL; - + + if (pg_start < 0 || pg_start + pg_count > (arena->size >> PAGE_SHIFT)) + return -EINVAL; + if (pg_start + pg_count < pg_start) + return -EINVAL; + spin_lock_irqsave(&arena->lock, flags);
ptes = arena->ptes; @@ -907,6 +917,11 @@ iommu_unbind(struct pci_iommu_arena *arena, long pg_start, long pg_count)
if (!arena) return -EINVAL;
+ if (pg_start < 0 || pg_start + pg_count > (arena->size >> PAGE_SHIFT)) + return -EINVAL; + if (pg_start + pg_count < pg_start) + return -EINVAL; + p = arena->ptes + pg_start; for(i = 0; i < pg_count; i++) p[i] = IOMMU_RESERVED_PTE; diff --git a/drivers/char/agp/alpha-agp.c b/drivers/char/agp/alpha-agp.c index e1763ecb8111..e2ab959662f3 100644 --- a/drivers/char/agp/alpha-agp.c +++ b/drivers/char/agp/alpha-agp.c @@ -93,7 +93,9 @@ static int alpha_core_agp_insert_memory(struct agp_memory *mem, off_t pg_start,
temp = agp_bridge->current_size; num_entries = A_SIZE_FIX(temp)->num_entries; - if ((pg_start + mem->page_count) > num_entries) + if (pg_start < 0 || (pg_start + mem->page_count) > num_entries) + return -EINVAL; + if ((pg_start + mem->page_count) < pg_start) return -EINVAL;
status = agp->ops->bind(agp, pg_start, mem); @@ -107,8 +109,17 @@ static int alpha_core_agp_remove_memory(struct agp_memory *mem, off_t pg_start, int type) { alpha_agp_info *agp = agp_bridge->dev_private_data; + int num_entries; + void *temp; int status;
+ temp = agp_bridge->current_size; + num_entries = A_SIZE_FIX(temp)->num_entries; + if (pg_start < 0 || (pg_start + mem->page_count) > num_entries) + return -EINVAL; + if ((pg_start + mem->page_count) < pg_start) + return -EINVAL; + status = agp->ops->unbind(agp, pg_start, mem); alpha_core_agp_tlbflush(mem); return status;
linux-stable-mirror@lists.linaro.org