From: sashiko-bot@kernel.org
To: "Thierry Reding" <thierry.reding@kernel.org>
Cc: media-ci@linuxtv.org, "Vasily Gorbik" <gor@linux.ibm.com>,
"Christian Borntraeger" <borntraeger@linux.ibm.com>,
"Alexander Gordeev" <agordeev@linux.ibm.com>,
linux-trace-kernel@vger.kernel.org, robh@kernel.org,
"Heiko Carstens" <hca@linux.ibm.com>,
dri-devel@lists.freedesktop.org, devicetree@vger.kernel.org,
linux-s390@vger.kernel.org, conor+dt@kernel.org
Subject: Re: [PATCH v6 09/12] dma-buf: heaps: Add support for Tegra VPR
Date: Fri, 04 Sep 2026 11:38:01 +0000 [thread overview]
Message-ID: <20260904113801.B10BA1F00A3D@smtp.kernel.org> (raw)
In-Reply-To: <20260904-tegra-vpr-v6-9-79042cfa8de5@nvidia.com>
Thank you for your contribution! Sashiko AI review found 15 potential issue(s) to consider:
- [High] Calling `cma_init_reserved_mem` on a `no-map` region causes a boot-time kernel panic.
- [High] Memory allocated with `kvmalloc_array()` is incorrectly freed with `kfree()`.
- [High] Calling `dma_map_sgtable()` with an SG list containing a NULL page maps garbage physical addresses.
- [High] Direct invocation of system PM callbacks (`pm_generic_freeze`/`thaw`) bypasses the PM core.
- [High] 0-sized chunks generate an integer underflow that permanently breaks VPR buffer allocation.
- [High] Pointer arithmetic on `struct page` arrays across MAX_ORDER / memory sections causes panics on SPARSEMEM.
- [High] Direct map corruption when returning partially invalidated pages to the CMA allocator.
- [High] Integer overflow in allocation size allows a zero-sized allocation that permanently bricks the heap.
- [High] Shrinking VPR restores linear mapping before the hardware protection is removed, risking speculative SErrors.
- [Medium] Memory and reference leaks on the error path of `tegra_vpr_add_heap()`.
- [Medium] Missing lock acquisition when traversing `vpr->buffers` list in `tegra_vpr_debugfs_show_chunks`.
- [Medium] Dereferencing `dev->driver->pm` without checking if `dev->driver` is valid.
- [Low] Off-by-one error in VPR end_page calculation permanently wastes the last page.
- [Low] Use of dynamically sized Variable Length Arrays (VLAs) via `DECLARE_BITMAP`.
- [Low] The commit message claims userspace processes are frozen during resize, but the code does not implement this.
--
commit 08e43ed119b9e546aa530f96844c1805cc1597a2
Author: Thierry Reding <treding@nvidia.com>
Subject: dma-buf: heaps: Add support for Tegra VPR
This commit introduces DMA-BUF heap support for the NVIDIA Tegra Video
Protection Region (VPR). It adds initialization for CMA memory during boot
and a heap driver for dynamically allocating and resizing chunks of this
protected memory.
> Unfortunately, resizing the VPR is slightly tricky because the GPU found
> on Tegra SoCs must be in reset during the VPR resize operation. This is
> currently implemented by freezing all userspace processes and calling
> invoking the GPU's freeze() implementation, resizing and the thawing the
> GPU and userspace processes. This is quite heavy-handed, so eventually
[Severity: Low]
The commit message states that userspace processes are frozen during resize,
but this doesn't seem to be implemented in the code (perhaps removed in v4?).
Could the commit message be updated to reflect the current behavior?
> diff --git a/drivers/dma-buf/heaps/tegra-vpr-init.c b/drivers/dma-buf/heaps/tegra-vpr-init.c
> --- /dev/null
> +++ b/drivers/dma-buf/heaps/tegra-vpr-init.c
> @@ -0,0 +1,133 @@
[ ... ]
> +static int __init tegra_vpr_node_init(unsigned long offset,
> + struct reserved_mem *rmem)
> +{
> + struct cma *cma;
> + int err;
> +
> + if (!IS_ALIGNED(rmem->base, SZ_1M)) {
> + pr_err("%s: base is not aligned to 1 MiB\n", rmem->name);
> + return -EINVAL;
> + }
> +
> + if (!IS_ALIGNED(rmem->size, SZ_1M)) {
> + pr_err("%s: size is not aligned to 1 MiB\n", rmem->name);
> + return -EINVAL;
> + }
> +
> + err = cma_init_reserved_mem(rmem->base, rmem->size, 0, rmem->name,
> + &cma);
[Severity: High]
Can this cause a panic during early boot if the firmware configures a fixed
VPR using the no-map property? A no-map region is removed from the linear
memory map (no struct page allocation), but cma_init_reserved_mem() is called
unconditionally. When CMA later tries to return pages to the buddy allocator
via pfn_to_page(), won't it access invalid pointers?
[ ... ]
> +static int tegra_vpr_device_init(struct reserved_mem *rmem, struct device *dev)
> +{
> + const struct dev_pm_ops *pm = dev->driver->pm;
[Severity: Medium]
Is it safe to blindly dereference dev->driver here? If this is called on a
device node without a bound driver, dev->driver would be NULL, causing a panic.
Should we verify dev->driver is valid first?
> diff --git a/drivers/dma-buf/heaps/tegra-vpr.c b/drivers/dma-buf/heaps/tegra-vpr.c
> --- /dev/null
> +++ b/drivers/dma-buf/heaps/tegra-vpr.c
> @@ -0,0 +1,1210 @@
[ ... ]
> +static int tegra_vpr_resize(struct tegra_vpr *vpr)
> +{
> + struct tegra_vpr_device *node;
> + phys_addr_t base, size;
> + int err, status = 0;
> +
> + err = tegra_vpr_get_extents(vpr, &base, &size);
> + if (err < 0) {
> + pr_err("%s(): failed to get VPR extents: %d\n", __func__, err);
> + return err;
> + }
> +
> + list_for_each_entry(node, &vpr->devices, node) {
> + err = pm_generic_freeze(node->dev);
[ ... ]
> + list_for_each_entry_continue_reverse(node, &vpr->devices, node) {
> + err = pm_generic_thaw(node->dev);
[Severity: High]
Does calling pm_generic_freeze() and pm_generic_thaw() directly bypass the
PM core's synchronization? Normally these are coordinated under device_lock.
If a device's driver is concurrently unbinding, dev->driver might become NULL
during the callback, which could lead to a kernel panic. Should this be
coordinated with the PM core instead?
[ ... ]
> +static bool tegra_vpr_chunk_overlaps(struct tegra_vpr_chunk *chunk,
> + unsigned int start, unsigned int limit)
> +{
> + unsigned int first = chunk->offset;
> + unsigned int last = chunk->offset + chunk->num_pages - 1;
[Severity: High]
Could chunk->num_pages be 0 for trailing chunks if the total VPR size doesn't
evenly divide into num_chunks?
Looking at tegra_vpr_setup_chunks():
size = min_t(size_t, size, max_size);
end = start + size - 1;
If the remaining size is 0, this would cause chunk->num_pages to be 0, making
the calculation underflow to UINT_MAX. This would cause the 0-sized chunk to
falsely overlap all future allocations and break the VPR heap.
[ ... ]
> +static int tegra_vpr_activate_chunks(struct tegra_vpr *vpr,
> + struct tegra_vpr_buffer *buffer)
> +{
> + DECLARE_BITMAP(dirty, vpr->num_chunks);
[Severity: Low]
Since vpr->num_chunks is evaluated at runtime, doesn't DECLARE_BITMAP create
a Variable Length Array (VLA) on the stack? The kernel globally forbids VLAs
and this will likely cause a build failure with -Werror=vla.
> + unsigned int i, bottom, top;
> + int err = 0, ret;
> +
> + bitmap_zero(dirty, vpr->num_chunks);
> +
> + /* activate any inactive chunks that overlap this buffer */
> + for_each_set_bit(i, buffer->chunks, vpr->num_chunks) {
> + struct tegra_vpr_chunk *chunk = &vpr->chunks[i];
> +
> + if (chunk->active)
> + continue;
> +
> + err = tegra_vpr_chunk_alloc(chunk);
> + if (err < 0)
> + goto deactivate;
> +
> + err = tegra_vpr_chunk_activate(chunk);
> + if (err < 0) {
> + tegra_vpr_chunk_release(chunk);
> + goto deactivate;
> + }
[Severity: High]
If set_direct_map_invalid_noflush() fails partially, tegra_vpr_chunk_activate()
returns an error, and the caller immediately invokes tegra_vpr_chunk_release().
Will this return partially invalidated pages back to the CMA allocator without
restoring the linear mapping? Subsequent allocations of these pages could
trigger direct map faults.
[ ... ]
> +static int tegra_vpr_find_free_region(struct tegra_vpr *vpr,
> + unsigned int num_pages,
> + unsigned long align)
> +{
> + return tegra_vpr_find_free_region_in_range(vpr, 0, vpr->num_pages - 1,
> + num_pages, align);
> +}
[Severity: Low]
Is there an off-by-one error here? tegra_vpr_find_free_region_in_range()
expects end_page as an exclusive upper bound, but passing vpr->num_pages - 1
means an allocation perfectly reaching the end of the region will be
incorrectly rejected.
[ ... ]
> +static struct tegra_vpr_buffer *
> +tegra_vpr_buffer_allocate(struct tegra_vpr *vpr, size_t size)
> +{
[ ... ]
> + buffer->pages = kvmalloc_array(buffer->num_pages,
> + sizeof(*buffer->pages),
> + GFP_KERNEL);
> + if (!buffer->pages) {
> + err = -ENOMEM;
> + goto free;
> + }
> +
> + for (i = 0; i < buffer->num_pages; i++)
> + buffer->pages[i] = &vpr->start_page[pageno + i];
[Severity: High]
Is it safe to perform direct pointer arithmetic on struct page arrays across
potentially massive VPR regions? On platforms using the SPARSEMEM memory model
without VMEMMAP, struct page objects are only contiguous within a single memory
section. A 2 GiB VPR region crosses many section boundaries, so this arithmetic
could point into unmapped memory and crash the kernel when accessed.
[ ... ]
> +static void tegra_vpr_buffer_release(struct tegra_vpr_buffer *buffer)
> +{
[ ... ]
> + kfree(buffer->pages);
> + kfree(buffer);
> +}
[Severity: High]
buffer->pages is allocated using kvmalloc_array(), which may fall back to
vmalloc space. Shouldn't this be freed with kvfree()? Calling kfree() on a
vmalloc'd pointer will cause a SLUB panic or memory corruption.
[ ... ]
> +static int tegra_vpr_attach(struct dma_buf *buf,
> + struct dma_buf_attachment *attachment)
> +{
[ ... ]
> + } else {
> + if (sg_alloc_table(&attach->sgt, 1, GFP_KERNEL)) {
> + err = -ENOMEM;
> + goto free;
> + }
> +
> + sg_set_page(attach->sgt.sgl, NULL, buffer->size, 0);
> + sg_dma_address(attach->sgt.sgl) = buffer->start;
> + sg_dma_len(attach->sgt.sgl) = buffer->size;
> + }
[Severity: High]
For static VPR setups, the SG list is populated with a NULL page. When
tegra_vpr_map_dma_buf() later calls dma_map_sgtable(), the IOMMU DMA mapping
code relies on sg_phys(), which calculates page_to_phys(NULL) + offset. This
usually results in physical address 0. The IOMMU will be programmed to map
address 0 instead of the actual VPR hardware address, breaking DMA.
[ ... ]
> +static void tegra_vpr_recycle(struct tegra_vpr *vpr)
> +{
> + DECLARE_BITMAP(dirty, vpr->num_chunks);
> + unsigned int i;
> + int err;
[Severity: Low]
Here is another DECLARE_BITMAP using vpr->num_chunks that creates a VLA.
[ ... ]
> + err = tegra_vpr_chunk_deactivate(chunk);
[ ... ]
> + if (!bitmap_empty(dirty, vpr->num_chunks)) {
> + err = tegra_vpr_resize(vpr);
[Severity: High]
tegra_vpr_chunk_deactivate() is called before tegra_vpr_resize(). This restores
the CPU's linear mapping to the memory while the hardware firewall is still
active. Does this inverted ordering create a race window where speculative CPU
accesses to the direct map could trigger a fatal bus fault / SError before
the hardware firewall is shrunk?
[ ... ]
> +static struct dma_buf *tegra_vpr_allocate(struct dma_heap *heap,
> + unsigned long len, u32 fd_flags,
> + u64 heap_flags)
> +{
> + struct tegra_vpr *vpr = dma_heap_get_drvdata(heap);
> + struct tegra_vpr_buffer *buffer, *entry;
> + size_t size = ALIGN(len, vpr->align);
[Severity: High]
Could ALIGN(len, vpr->align) overflow to 0 if an unprivileged user requests a
massive len? A 0-sized allocation might succeed and eventually underflow
vpr->last when it computes vpr->last = limit - 1 with limit = 0. This would
permanently corrupt the heap tracking bounds and break future clustered
allocations.
[ ... ]
> +static void tegra_vpr_debugfs_show_chunks(struct tegra_vpr *vpr,
> + struct seq_file *s)
> +{
> + struct tegra_vpr_buffer *buffer;
> + unsigned int i;
> + char buf[16];
> +
> + for (i = 0; i < vpr->num_chunks; i++) {
> + const struct tegra_vpr_chunk *chunk = &vpr->chunks[i];
> +
> + string_get_size(chunk->size, 1, STRING_UNITS_2, buf,
> + sizeof(buf));
> + seq_printf(s, " %pap-%pap (%s) (%s, %u buffers)\n",
> + &chunk->start, &chunk->limit, buf,
> + chunk->active ? "active" : "inactive",
> + chunk->num_buffers);
> + }
> +
> + list_for_each_entry(buffer, &vpr->buffers, list) {
[Severity: Medium]
This iterates over the vpr->buffers linked list without holding vpr->lock.
If a user reads this debugfs file concurrently with heap allocations or frees,
could this race with list mutations and cause use-after-free or list
corruption panics?
[ ... ]
> +static int tegra_vpr_add_heap(struct reserved_mem *rmem,
> + struct device_node *np)
> +{
[ ... ]
> + vpr->dev_node = of_node_get(np);
[ ... ]
> + if (vpr->resizable)
> + err = tegra_vpr_setup_chunks(vpr, rmem->name);
> + else
> + err = tegra_vpr_setup_static(vpr);
> +
> + if (err < 0)
> + goto free;
[ ... ]
> +free:
> + bitmap_free(vpr->bitmap);
> + kfree(vpr);
> + return err;
> +}
[Severity: Medium]
There appear to be multiple leaks in the error paths here. If
tegra_vpr_setup_chunks() fails, it jumps to free without calling
of_node_put(vpr->dev_node). Additionally, if dma_heap_add() fails, the cleanup
path calls tegra_vpr_free_chunks(), which correctly frees the chunks array but
forgets to free the vpr->active bitmap allocated during setup.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260904-tegra-vpr-v6-0-79042cfa8de5@nvidia.com?part=9
next prev parent reply other threads:[~2026-09-04 11:38 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-04 10:44 [PATCH v6 00/12] dma-buf: heaps: Add support for Tegra VPR Thierry Reding
2026-09-04 10:44 ` [PATCH v6 01/12] dt-bindings: reserved-memory: Document " Thierry Reding
2026-09-04 10:59 ` sashiko-bot
2026-09-04 10:44 ` [PATCH v6 02/12] dt-bindings: display: tegra: Document memory regions Thierry Reding
2026-09-04 10:56 ` sashiko-bot
2026-09-04 10:44 ` [PATCH v6 03/12] dt-bindings: gpu: host1x: Document memory-regions for NVDEC Thierry Reding
2026-09-04 11:03 ` sashiko-bot
2026-09-04 10:44 ` [PATCH v6 04/12] arm64/mm: Export set_direct_map_*_noflush() APIs Thierry Reding
2026-09-04 11:13 ` sashiko-bot
2026-09-04 10:44 ` [PATCH v6 05/12] bitmap: Add bitmap_allocate() function Thierry Reding
2026-09-04 11:13 ` sashiko-bot
2026-09-04 10:44 ` [PATCH v6 06/12] of: Export of_node_to_nid() Thierry Reding
2026-09-04 11:21 ` sashiko-bot
2026-09-04 10:44 ` [PATCH v6 07/12] mm/cma: Introduce cma_alloc_at() API Thierry Reding
2026-09-04 11:22 ` sashiko-bot
2026-09-04 10:44 ` [PATCH v6 08/12] dma-buf: heaps: Add debugfs support Thierry Reding
2026-09-04 11:34 ` sashiko-bot
2026-09-04 10:45 ` [PATCH v6 09/12] dma-buf: heaps: Add support for Tegra VPR Thierry Reding
2026-09-04 11:38 ` sashiko-bot [this message]
2026-09-04 10:45 ` [PATCH v6 10/12] arm64: tegra: Add VPR placeholder node on Tegra234 Thierry Reding
2026-09-04 11:54 ` sashiko-bot
2026-09-04 10:45 ` [PATCH v6 11/12] arm64: tegra: Hook up VPR to host1x Thierry Reding
2026-09-04 11:49 ` sashiko-bot
2026-09-04 10:45 ` [PATCH v6 12/12] arm64: tegra: Add VPR placeholder node on Tegra264 Thierry Reding
2026-09-04 11:53 ` sashiko-bot
2026-09-04 11:41 ` [PATCH v6 00/12] dma-buf: heaps: Add support for Tegra VPR Will Deacon
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260904113801.B10BA1F00A3D@smtp.kernel.org \
--to=sashiko-bot@kernel.org \
--cc=agordeev@linux.ibm.com \
--cc=borntraeger@linux.ibm.com \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=dri-devel@lists.freedesktop.org \
--cc=gor@linux.ibm.com \
--cc=hca@linux.ibm.com \
--cc=linux-s390@vger.kernel.org \
--cc=linux-trace-kernel@vger.kernel.org \
--cc=media-ci@linuxtv.org \
--cc=robh@kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
--cc=thierry.reding@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox