From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-qt1-f179.google.com (mail-qt1-f179.google.com [209.85.160.179]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 303143A1E8C for ; Sun, 14 Jun 2026 13:11:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.179 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781442668; cv=none; b=hO/Uynxo5oRwcvcCc4wRyKSa1fM187RUVm5jzsh34NP+GV2hIddASwoG9tSeVVqVlOgruOKrteq2WBRiU0kjlz0AjooCnSvnCFs5JoJ3OKW+FFv7oGZcSkca3uGuuu4qGiXUtGelesREtiA4LlI0IUMxlBcRSweslHw1B6fKpDA= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781442668; c=relaxed/simple; bh=/3W8mqtsnJOdLmYOP7SmPnyNbzA54TUL70lneUYYoqQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=iyLQCzvahEH9k/PYqcq5zciLtC6Wu2KFoIc3RNOup21r9ahXNjFLc3KA84dLnw9+HLmwLStrcYOSg1EgtmaYWp1xP7OojdguOv8JczjGN26kDNWaffqs/+79sRizgLOu4sJu5WAjFOiCl1EtgBAAADUOnxFgOq9sHoxX9M+9ejg= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=mZEYjzxf; arc=none smtp.client-ip=209.85.160.179 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="mZEYjzxf" Received: by mail-qt1-f179.google.com with SMTP id d75a77b69052e-51776b4de37so21791511cf.1 for ; Sun, 14 Jun 2026 06:11:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1781442666; x=1782047466; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=yzg/YOIIjYEH15Di+UXqiKscgR5aDMncmsvi0n7iLLw=; b=mZEYjzxfTbFceP6mI/JtHC6x5cI/XI0vFMmUgUQ3WJt1/ZejNgN8EoC1HxOi7XLzIo pnXh4n8AbpBvLqK6YTEp2JlMWmVN37UgcusyxLx86bu8Q2e6O6GaULuSSCOYAzndIW8R obZDOi5HEOda/xrc1yWkVymVDcZOUslfcti5m7sDc3pw7CPegEayIYfF6u5oXOoKsm7F PV2Cd2yubd6KjW2v4biL/pA/k7EN17EB3rCSnCqoJOUQ8rvFiIlKRNFudutSGuGBUiel iOkLKZ8UeN7JjLDo6+qZqvpfOjZLOx+HARI1DD6d3YZvTdSovP8e5f0L4CRmNir9hLpw fPWQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781442666; x=1782047466; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=yzg/YOIIjYEH15Di+UXqiKscgR5aDMncmsvi0n7iLLw=; b=lZaQ0ZECS3T596ojkcb49TnKFpO2cOjmXTHLfwCXh12j+lNFRnCGLeFyzrY/lLXgRs YIkOWpQC1OkoNqvOmFH1zFPF1FIIABb5uH4WnNPLhKXBS4QHj/P1Xqw3w2c3W0nFsmB3 aVifIj4a1XyEAodfLW97AFW68jW9yMaXzqBm/IH3S45epLYBQrcskMG7xo0MHaFykGiK WrtmPRdC2UzZMyIZquQTE+uItW+MfaoL8WsO8JH8/WNB8wEQxWMLU9Yqz7G3+5eT0Q4A IJLeMUeh6EWvhLEae3F/NQ+U9VKlmC6fACfDXafksFq3qeFAya0MD85zhg5TuYJgdz1W 3mKg== X-Forwarded-Encrypted: i=1; AFNElJ9Uope0AKHKJ9RJ89lE46jRaUTNSrhEoQI1Zude3f6alQlIlJFuz7wr/M80UVN5JS+hnXY1lAcgGDqntjM=@vger.kernel.org X-Gm-Message-State: AOJu0YymXM0UHGUeUTTA/hJtHzeDrMzl8+cNWlmeUBkk/H6naCekIpq5 YIZXgTtEM6Dsfv0Wd1+vnoL6d8NM9NDDKF1HWNbodU1kuFI3YNUysDKO X-Gm-Gg: Acq92OFPZuDBF5fYj0tEvpbSTok2HGpyEIjSEl5Kk+NIE2CBwwzKoEcdakORNlMbI8i vGMb6Gt94yKvtQDSprICY8leDohvXuizWQGKOqdc8nY9yogMnCwfNIrafiSDd1na/g0Wr1OyjoB ooFLyuXFMDnx41LigssB6zzvzIhYUyyvPOlVRvyzfj9osBysUwb+3nsT/UJRiIan13z6FWbla89 MRmHyW5LqFeTA5hjR+jrmR6jZsVXXHDIeQB67Pi08siOlQvAGGigodqvEaBB85ACIPVtJXr8e+P ljYrDwS2RzLVhIUkdhGUvAC+0DieN8u207gRIf1GJAbPTjFfwrINe7VsspkCtxJfr4FdNsQwyIt u2kOCjtDcq7taNiLtwDxar8TVkv9cex9Pf3jmlGSIJmTUHnoFzyK5Etgo8t+g7Zy/8quOXJAnKl R8RcqkaSdiAyc49eNer9rKWyEfHGQ80IJwYkEOQ7BQe59Pu54O45Mu2RGIkKaCdy7yRhxL+xkU5 MQ7nybCQn0FaC7e2usG+o8OWaorvFmlUVyBKnAIAyo= X-Received: by 2002:a05:622a:1456:b0:517:5beb:9b17 with SMTP id d75a77b69052e-519533573cemr115073121cf.1.1781442666031; Sun, 14 Jun 2026 06:11:06 -0700 (PDT) Received: from server0.tail6e7dd.ts.net (c-68-48-65-54.hsd1.mi.comcast.net. [68.48.65.54]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-5196073d7fbsm25145811cf.30.2026.06.14.06.11.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 14 Jun 2026 06:11:05 -0700 (PDT) From: Michael Bommarito To: Melissa Wen , Maira Canal Cc: Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter , Iago Toral Quiroga , dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org Subject: [PATCH 1/2] drm/v3d: validate copy-query buffer bounds against destination BO size Date: Sun, 14 Jun 2026 09:10:59 -0400 Message-ID: <20260614131100.2525157-2-michael.bommarito@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260614131100.2525157-1-michael.bommarito@gmail.com> References: <20260614131100.2525157-1-michael.bommarito@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 7bit The V3D_SUBMIT_CPU COPY_TIMESTAMP_QUERY and COPY_PERFORMANCE_QUERY extensions store a user-supplied destination offset, per-query stride and query count on the job and consume them at exec without checking that offset + (count - 1) * stride + per-query write size stays inside the destination BO. The copy then writes counter values and the availability bit past the BO's vmap mapping; the timestamp variant also reads each result from an unchecked offset into the second BO. A render-node user (DRM_RENDER_ALLOW, no master, no capability) controls the offset. Validate the full write extent against the destination BO size once the BOs are looked up, before the job is queued, rejecting overflow or out-of-range geometry with -EINVAL; the per-query write size is computed with check_*_overflow() so a u32 product cannot wrap the bound, and the timestamp source offsets are bounded in the same pass. A KUnit reproducer follows. Fixes: 6745f3e44a20 ("drm/v3d: Create a CPU job extension to copy timestamp query to a buffer") Signed-off-by: Michael Bommarito Assisted-by: Claude:claude-opus-4-8 --- Reproduced under KASAN via a KUnit (patch 2) driving the real v3d_copy_query_results() over a shmem-backed BO; a copy offset at the BO size writes past the one-page vmap mapping: BUG: KASAN: vmalloc-out-of-bounds in v3d_copy_query_results+0x807/0x900 The trigger faults on stock and is rejected at submit time on the patched tree; two in-bounds controls pass on both. The performance-query copy shares the bound; its write values come from perfmon counters and were checked by source review rather than this hardware-free run. drivers/gpu/drm/v3d/v3d_submit.c | 86 ++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/drivers/gpu/drm/v3d/v3d_submit.c b/drivers/gpu/drm/v3d/v3d_submit.c index ee4512db294b3..23e19dacfdce2 100644 --- a/drivers/gpu/drm/v3d/v3d_submit.c +++ b/drivers/gpu/drm/v3d/v3d_submit.c @@ -1246,6 +1246,88 @@ static const unsigned int cpu_job_bo_handle_count[] = { [V3D_CPU_JOB_TYPE_COPY_PERFORMANCE_QUERY] = 1, }; +/* Reject offset + (count - 1) * stride + write_size if it leaves the BO. */ +static int +v3d_check_copy_extent(struct drm_device *dev, size_t bo_size, + u32 offset, u32 stride, u32 count, u32 write_size) +{ + u32 span, last; + + if (!count) + return 0; + + if (check_mul_overflow(stride, count - 1, &span) || + check_add_overflow(span, write_size, &span) || + check_add_overflow(span, offset, &last) || + last > bo_size) { + drm_dbg(dev, "CPU job copy buffer exceeds the destination BO.\n"); + return -EINVAL; + } + + return 0; +} + +/* Bound the copy-query CPU-job writes; the exec-time copy does not. */ +static int +v3d_cpu_job_check_copy_bounds(struct v3d_cpu_job *job) +{ + struct drm_device *dev = &job->base.v3d->drm; + struct v3d_copy_query_results_info *copy = &job->copy; + u32 elem = copy->do_64bit ? sizeof(u64) : sizeof(u32); + struct v3d_bo *bo, *timestamp; + u32 slots, write_size; + int i; + + switch (job->job_type) { + case V3D_CPU_JOB_TYPE_COPY_TIMESTAMP_QUERY: + bo = to_v3d_bo(job->base.bo[0]); + timestamp = to_v3d_bo(job->base.bo[1]); + + slots = copy->availability_bit ? 2 : 1; + if (check_mul_overflow(slots, elem, &write_size)) + return -EINVAL; + + if (v3d_check_copy_extent(dev, bo->base.base.size, copy->offset, + copy->stride, job->timestamp_query.count, + write_size)) + return -EINVAL; + + for (i = 0; i < job->timestamp_query.count; i++) { + u32 end; + + if (check_add_overflow(job->timestamp_query.queries[i].offset, + (u32)sizeof(u64), &end) || + end > timestamp->base.base.size) { + drm_dbg(dev, "CPU job timestamp query offset exceeds the BO.\n"); + return -EINVAL; + } + } + return 0; + case V3D_CPU_JOB_TYPE_COPY_PERFORMANCE_QUERY: + bo = to_v3d_bo(job->base.bo[0]); + + if (check_mul_overflow(job->performance_query.nperfmons, + (u32)DRM_V3D_MAX_PERF_COUNTERS, &slots)) + return -EINVAL; + if (copy->availability_bit) { + u32 avail_slots; + + if (check_add_overflow(job->performance_query.ncounters, + 1u, &avail_slots)) + return -EINVAL; + slots = max(slots, avail_slots); + } + if (check_mul_overflow(slots, elem, &write_size)) + return -EINVAL; + + return v3d_check_copy_extent(dev, bo->base.base.size, copy->offset, + copy->stride, job->performance_query.count, + write_size); + default: + return 0; + } +} + /** * v3d_submit_cpu_ioctl() - Submits a CPU job to the V3D. * @dev: DRM device @@ -1317,6 +1399,10 @@ v3d_submit_cpu_ioctl(struct drm_device *dev, void *data, if (ret) goto fail; + ret = v3d_cpu_job_check_copy_bounds(cpu_job); + if (ret) + goto fail; + ret = v3d_lock_bo_reservations(&cpu_job->base, &acquire_ctx); if (ret) goto fail; -- 2.53.0