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;
On Fri, Oct 24, 2025 at 5:48 AM Yuhao Jiang danisjiang@gmail.com wrote:
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.
I guess the bounds checking in the AGP code for Alpha has some limitations as to how it's implemented. I spent some time looking at how bounds checking in alpha_core_agp_insert_memory() is done on other architectures and I see some of the same issues in for, example parisc_agp_insert_memory() as well as amd64_insert_memory(), which even has a /* FIXME: could wrap */ line at its bounds checking code. I guess even agp_generic_insert_memory() has similar limitations. I'm wondering if this is the case, because at some point, it was determined that this will never become a real problem and no need to mess with old code that isn't really that broken, or just that no one ever got around to fixing it properly?
If it needs fixing, should we try to fix it for all arch's that have similar limitations?
Magnus
linux-stable-mirror@lists.linaro.org