AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/09/26 15:28 flow patch-triage
7m Args:
null

Results:
{
  "EnableConfigs": [
    "DMABUF_HEAPS",
    "DMABUF_HEAPS_SYSTEM",
    "DMABUF_HEAPS_SYSTEM_CC_SHARED",
    "DMA_CMA",
    "CMA",
    "AMD_MEM_ENCRYPT"
  ],
  "FocusSymbols": [
    "system_heap_allocate",
    "system_heap_dma_buf_release",
    "dma_alloc_contiguous",
    "dma_alloc_from_contiguous",
    "atomic_pool_expand"
  ],
  "KMSANReasoning": "The patch series consolidates confidential computing (CC) shared/decrypted memory management and pushes zeroing into architecture-specific `set_memory_decrypted()` hooks. However, in `drivers/hv/hv.c:hv_alloc_page()`, the patch changes the unconditional `memset(*page, 0, PAGE_SIZE)` to only execute in the `else` branch of `if (decrypt)`:\n\n```c\n\tif (decrypt) {\n\t\tret = set_memory_decrypted((unsigned long)*page, 1);\n\t\tif (ret)\n\t\t\tgoto failed;\n\t} else {\n\t\tmemset(*page, 0, PAGE_SIZE);\n\t}\n```\n\nIn standard non-confidential Hyper-V guest VMs, `vmbus_is_confidential()` returns `false`, causing `hv_synic_alloc()` to set `decrypt = true`. On such systems, `cc_platform_has(CC_ATTR_MEM_ENCRYPT)` is false, meaning `set_memory_decrypted()` is a no-op that returns 0 without clearing the page. Consequently, `hv_alloc_page()` leaves `hyp_synic_message_page` and `hyp_synic_event_page` allocated via `__get_free_page(GFP_KERNEL)` completely uninitialized.\n\nWhen Hyper-V message DPC handling runs (`vmbus_drv.c:__vmbus_on_msg_dpc`), the kernel reads `msg = (struct hv_message *)message_page_addr + VMBUS_MESSAGE_SINT;` and branches on `msg_copy.header.message_type == HVMSG_NONE` and subsequent channel message headers using uninitialized memory.\n\nKASAN only tracks address bounds and object lifetime (use-after-free, out-of-bounds); because the allocated page is a valid memory object, KASAN cannot detect reads from uninitialized page contents or conditional logic branching on uninitialized data. Only KMSAN tracks page initialization shadow state and detects control flow / logic dependencies on uninitialized memory. Thus, a dedicated KMSAN fuzzing session is justified.",
  "NeedsKMSAN": true,
  "Reasoning": "The patch series introduces shared page allocation and alignment helpers for confidential computing, while refactoring memory allocation and release paths across the DMA contiguous memory allocator (CMA), atomic DMA pools, and the DMA-buf system heap (drivers/dma-buf/heaps/system_heap.c). These changes modify reachable code paths on amd64, including user-accessible dma_heap allocation/release logic and CMA buffer allocation.",
  "WorthFuzzing": true
}

1/1 2026/09/26 15:28 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 4a46b87645d47c6cdb58e9df76e3a9445bb4efbd\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Sat Sep 26 15:28:34 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c\nindex 7761099dde9e8..9714fcd519418 100644\n--- a/arch/arm/mm/dma-mapping.c\n+++ b/arch/arm/mm/dma-mapping.c\n@@ -398,7 +398,8 @@ static void *__alloc_from_contiguous(struct device *dev, size_t size,\n \tstruct page *page;\n \tvoid *ptr = NULL;\n \n-\tpage = dma_alloc_from_contiguous(dev, count, order, gfp \u0026 __GFP_NOWARN);\n+\tpage = dma_alloc_from_contiguous(dev, count, order, 0,\n+\t\t\t\t\t gfp \u0026 __GFP_NOWARN);\n \tif (!page)\n \t\treturn NULL;\n \n@@ -866,7 +867,7 @@ static struct page **__iommu_alloc_buffer(struct device *dev, size_t size,\n \t\tunsigned long order = get_order(size);\n \t\tstruct page *page;\n \n-\t\tpage = dma_alloc_from_contiguous(dev, count, order,\n+\t\tpage = dma_alloc_from_contiguous(dev, count, order, 0,\n \t\t\t\t\t\t gfp \u0026 __GFP_NOWARN);\n \t\tif (!page)\n \t\t\tgoto error;\ndiff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c\nindex bbe98ac9ad8c6..ae1f5de66fad7 100644\n--- a/arch/arm64/mm/pageattr.c\n+++ b/arch/arm64/mm/pageattr.c\n@@ -339,6 +339,9 @@ static int realm_set_memory_decrypted(unsigned long addr, int numpages)\n {\n \tint ret = __set_memory_enc_dec(addr, numpages, false);\n \n+\tif (!ret)\n+\t\tmemset((void *)addr, 0, (size_t)numpages \u003c\u003c PAGE_SHIFT);\n+\n \tWARN(ret, \"Failed to decrypt memory, %d pages will be leaked\",\n \t     numpages);\n \ndiff --git a/arch/powerpc/platforms/pseries/svm.c b/arch/powerpc/platforms/pseries/svm.c\nindex 7a403dbd35ee5..a344e094fe1c8 100644\n--- a/arch/powerpc/platforms/pseries/svm.c\n+++ b/arch/powerpc/platforms/pseries/svm.c\n@@ -9,6 +9,7 @@\n #include \u003clinux/mm.h\u003e\n #include \u003clinux/memblock.h\u003e\n #include \u003clinux/mem_encrypt.h\u003e\n+#include \u003clinux/string.h\u003e\n #include \u003clinux/cc_platform.h\u003e\n #include \u003casm/machdep.h\u003e\n #include \u003casm/svm.h\u003e\n@@ -59,6 +60,7 @@ int set_memory_decrypted(unsigned long addr, int numpages)\n \tif (!PAGE_ALIGNED(addr))\n \t\treturn -EINVAL;\n \n+\tmemset((void *)addr, 0, (size_t)numpages \u003c\u003c PAGE_SHIFT);\n \tuv_share_page(PHYS_PFN(__pa(addr)), numpages);\n \n \treturn 0;\ndiff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c\nindex be7e009e7b59e..f2e28a2710e04 100644\n--- a/arch/s390/mm/init.c\n+++ b/arch/s390/mm/init.c\n@@ -129,6 +129,9 @@ int set_memory_encrypted(unsigned long vaddr, int numpages)\n int set_memory_decrypted(unsigned long vaddr, int numpages)\n {\n \tint i;\n+\n+\tmemset((void *)vaddr, 0, (size_t)numpages \u003c\u003c PAGE_SHIFT);\n+\n \t/* make specified pages shared (swiotlb, dma_alloca) */\n \tfor (i = 0; i \u003c numpages; ++i) {\n \t\tuv_set_shared(virt_to_phys((void *)vaddr));\ndiff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c\nindex f904a636d449b..1f1f390823911 100644\n--- a/arch/x86/coco/tdx/tdx.c\n+++ b/arch/x86/coco/tdx/tdx.c\n@@ -978,6 +978,9 @@ static bool tdx_enc_status_changed(unsigned long vaddr, int numpages, bool enc)\n static int tdx_enc_status_change_prepare(unsigned long vaddr, int numpages,\n \t\t\t\t\t bool enc)\n {\n+\tif (!enc)\n+\t\tmemset((void *)vaddr, 0, (size_t)numpages \u003c\u003c PAGE_SHIFT);\n+\n \t/*\n \t * Only handle shared-\u003eprivate conversion here.\n \t * See the comment in tdx_early_init().\ndiff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c\nindex 0b4a1c0b0b16f..a42468e81c344 100644\n--- a/arch/x86/hyperv/hv_init.c\n+++ b/arch/x86/hyperv/hv_init.c\n@@ -156,8 +156,10 @@ static int hv_cpu_init(unsigned int cpu)\n \t\t\t * page in non-root partition here.\n \t\t\t */\n \t\t\tif (*hvp \u0026\u0026 !ms_hyperv.paravisor_present \u0026\u0026 hv_isolation_type_snp()) {\n-\t\t\t\tWARN_ON_ONCE(set_memory_decrypted((unsigned long)(*hvp), 1));\n-\t\t\t\tmemset(*hvp, 0, PAGE_SIZE);\n+\t\t\t\tint ret;\n+\n+\t\t\t\tret = set_memory_decrypted((unsigned long)*hvp, 1);\n+\t\t\t\tWARN_ON_ONCE(ret);\n \t\t\t}\n \t\t}\n \ndiff --git a/arch/x86/hyperv/ivm.c b/arch/x86/hyperv/ivm.c\nindex 2ce4dfe534727..e9c7799a00943 100644\n--- a/arch/x86/hyperv/ivm.c\n+++ b/arch/x86/hyperv/ivm.c\n@@ -755,6 +755,10 @@ static int hv_mark_gpa_visibility(u16 count, const u64 pfn[],\n  */\n static int hv_vtom_clear_present(unsigned long kbuffer, int pagecount, bool enc)\n {\n+\tif (!enc)\n+\t\tmemset((void *)kbuffer, 0,\n+\t\t       (size_t)pagecount \u003c\u003c PAGE_SHIFT);\n+\n \treturn set_memory_np(kbuffer, pagecount);\n }\n \ndiff --git a/arch/x86/kernel/kvmclock.c b/arch/x86/kernel/kvmclock.c\nindex cb3d0ca1fa225..3e9b7ad3dea2b 100644\n--- a/arch/x86/kernel/kvmclock.c\n+++ b/arch/x86/kernel/kvmclock.c\n@@ -248,7 +248,7 @@ static void __init kvmclock_init_mem(void)\n \t * be mapped decrypted.\n \t */\n \tif (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) {\n-\t\tr = set_memory_decrypted((unsigned long) hvclock_mem,\n+\t\tr = set_memory_decrypted((unsigned long)hvclock_mem,\n \t\t\t\t\t 1UL \u003c\u003c order);\n \t\tif (r) {\n \t\t\t__free_pages(p, order);\n@@ -256,9 +256,9 @@ static void __init kvmclock_init_mem(void)\n \t\t\tpr_warn(\"kvmclock: set_memory_decrypted() failed. Disabling\\n\");\n \t\t\treturn;\n \t\t}\n+\t} else {\n+\t\tmemset(hvclock_mem, 0, PAGE_SIZE \u003c\u003c order);\n \t}\n-\n-\tmemset(hvclock_mem, 0, PAGE_SIZE \u003c\u003c order);\n }\n \n static int __init kvm_setup_vsyscall_timeinfo(void)\ndiff --git a/arch/x86/mm/mem_encrypt_amd.c b/arch/x86/mm/mem_encrypt_amd.c\nindex 2f8c32173972d..47cae102acd24 100644\n--- a/arch/x86/mm/mem_encrypt_amd.c\n+++ b/arch/x86/mm/mem_encrypt_amd.c\n@@ -13,6 +13,7 @@\n #include \u003clinux/dma-direct.h\u003e\n #include \u003clinux/swiotlb.h\u003e\n #include \u003clinux/mem_encrypt.h\u003e\n+#include \u003clinux/string.h\u003e\n #include \u003clinux/device.h\u003e\n #include \u003clinux/kernel.h\u003e\n #include \u003clinux/bitops.h\u003e\n@@ -285,6 +286,9 @@ static void enc_dec_hypercall(unsigned long vaddr, unsigned long size, bool enc)\n \n static int amd_enc_status_change_prepare(unsigned long vaddr, int npages, bool enc)\n {\n+\tif (!enc)\n+\t\tmemset((void *)vaddr, 0, (size_t)npages \u003c\u003c PAGE_SHIFT);\n+\n \t/*\n \t * To maintain the security guarantees of SEV-SNP guests, make sure\n \t * to invalidate the memory before encryption attribute is cleared.\ndiff --git a/drivers/dma-buf/heaps/system_heap.c b/drivers/dma-buf/heaps/system_heap.c\nindex c8959eadc71d1..5970b76416ae5 100644\n--- a/drivers/dma-buf/heaps/system_heap.c\n+++ b/drivers/dma-buf/heaps/system_heap.c\n@@ -11,14 +11,13 @@\n  */\n \n #include \u003clinux/cc_platform.h\u003e\n+#include \u003clinux/cc_shared.h\u003e\n #include \u003clinux/dma-buf.h\u003e\n #include \u003clinux/dma-mapping.h\u003e\n #include \u003clinux/dma-heap.h\u003e\n #include \u003clinux/err.h\u003e\n #include \u003clinux/highmem.h\u003e\n-#include \u003clinux/mem_encrypt.h\u003e\n #include \u003clinux/mm.h\u003e\n-#include \u003clinux/set_memory.h\u003e\n #include \u003clinux/module.h\u003e\n #include \u003clinux/pgtable.h\u003e\n #include \u003clinux/scatterlist.h\u003e\n@@ -65,34 +64,6 @@ static gfp_t order_flags[] = {HIGH_ORDER_GFP, HIGH_ORDER_GFP, LOW_ORDER_GFP};\n static const unsigned int orders[] = {8, 4, 0};\n #define NUM_ORDERS ARRAY_SIZE(orders)\n \n-static int system_heap_set_page_decrypted(struct page *page)\n-{\n-\tunsigned long addr = (unsigned long)page_address(page);\n-\tunsigned int nr_pages = 1 \u003c\u003c compound_order(page);\n-\tint ret;\n-\n-\tret = set_memory_decrypted(addr, nr_pages);\n-\tif (ret)\n-\t\tpr_warn_ratelimited(\"dma-buf system heap: failed to decrypt page at %p\\n\",\n-\t\t\t\t    page_address(page));\n-\n-\treturn ret;\n-}\n-\n-static int system_heap_set_page_encrypted(struct page *page)\n-{\n-\tunsigned long addr = (unsigned long)page_address(page);\n-\tunsigned int nr_pages = 1 \u003c\u003c compound_order(page);\n-\tint ret;\n-\n-\tret = set_memory_encrypted(addr, nr_pages);\n-\tif (ret)\n-\t\tpr_warn_ratelimited(\"dma-buf system heap: failed to re-encrypt page at %p, leaking memory\\n\",\n-\t\t\t\t    page_address(page));\n-\n-\treturn ret;\n-}\n-\n static int dup_sg_table(struct sg_table *from, struct sg_table *to)\n {\n \tstruct scatterlist *sg, *new_sg;\n@@ -337,6 +308,20 @@ static void system_heap_vunmap(struct dma_buf *dmabuf, struct iosys_map *map)\n \tiosys_map_clear(map);\n }\n \n+static void system_heap_free_page(struct page *page, bool cc_shared)\n+{\n+\tstruct cc_shared_pages mem;\n+\n+\tif (!cc_shared) {\n+\t\t__free_pages(page, compound_order(page));\n+\t\treturn;\n+\t}\n+\n+\tmem.page = page;\n+\tmem.shared_size = page_size(page);\n+\tfree_cc_shared_pages(\u0026mem);\n+}\n+\n static void system_heap_dma_buf_release(struct dma_buf *dmabuf)\n {\n \tstruct system_heap_buffer *buffer = dmabuf-\u003epriv;\n@@ -345,19 +330,8 @@ static void system_heap_dma_buf_release(struct dma_buf *dmabuf)\n \tint i;\n \n \ttable = \u0026buffer-\u003esg_table;\n-\tfor_each_sgtable_sg(table, sg, i) {\n-\t\tstruct page *page = sg_page(sg);\n-\n-\t\t/*\n-\t\t * Intentionally leak pages that cannot be re-encrypted\n-\t\t * to prevent shared memory from being reused.\n-\t\t */\n-\t\tif (cc_shared_buffer(buffer) \u0026\u0026\n-\t\t    system_heap_set_page_encrypted(page))\n-\t\t\tcontinue;\n-\n-\t\t__free_pages(page, compound_order(page));\n-\t}\n+\tfor_each_sgtable_sg(table, sg, i)\n+\t\tsystem_heap_free_page(sg_page(sg), cc_shared_buffer(buffer));\n \tsg_free_table(table);\n \tkfree(buffer);\n }\n@@ -375,22 +349,39 @@ static const struct dma_buf_ops system_heap_buf_ops = {\n \t.release = system_heap_dma_buf_release,\n };\n \n+static struct page *system_heap_alloc_order(unsigned int order,\n+\t     gfp_t flags, bool cc_shared)\n+{\n+\tstruct cc_shared_pages mem;\n+\n+\tif (!cc_shared)\n+\t\treturn alloc_pages(flags, order);\n+\n+\t/* The shared granule can raise the actual allocation order. */\n+\tflags |= __GFP_COMP;\n+\tif (alloc_cc_shared_pages(flags, PAGE_SIZE \u003c\u003c order, \u0026mem))\n+\t\treturn NULL;\n+\n+\treturn mem.page;\n+}\n+\n static struct page *alloc_largest_available(unsigned long size,\n-\t\t\t\t\t    unsigned int max_order)\n+\t\tunsigned int max_order, bool cc_shared)\n {\n \tstruct page *page;\n-\tint i;\n \tgfp_t flags;\n+\tint i;\n \n \tfor (i = 0; i \u003c NUM_ORDERS; i++) {\n \t\tif (size \u003c  (PAGE_SIZE \u003c\u003c orders[i]))\n \t\t\tcontinue;\n \t\tif (max_order \u003c orders[i])\n \t\t\tcontinue;\n+\n \t\tflags = order_flags[i];\n \t\tif (mem_accounting)\n \t\t\tflags |= __GFP_ACCOUNT;\n-\t\tpage = alloc_pages(flags, orders[i]);\n+\t\tpage = system_heap_alloc_order(orders[i], flags, cc_shared);\n \t\tif (!page)\n \t\t\tcontinue;\n \t\treturn page;\n@@ -405,7 +396,9 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,\n {\n \tstruct system_heap_buffer *buffer;\n \tDEFINE_DMA_BUF_EXPORT_INFO(exp_info);\n+\tstruct cc_shared_layout layout;\n \tunsigned long size_remaining = len;\n+\tunsigned long sg_remaining = len;\n \tunsigned int max_order = orders[0];\n \tstruct system_heap_priv *priv = dma_heap_get_drvdata(heap);\n \tbool cc_shared = priv-\u003ecc_shared;\n@@ -416,6 +409,16 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,\n \tstruct page *page, *tmp_page;\n \tint i, ret = -ENOMEM;\n \n+\tif (cc_shared) {\n+\t\tint err;\n+\n+\t\terr = cc_shared_calc_layout(len, \u0026layout);\n+\t\tif (err)\n+\t\t\treturn ERR_PTR(err);\n+\n+\t\tsize_remaining = layout.shared_size;\n+\t}\n+\n \tbuffer = kzalloc_obj(*buffer);\n \tif (!buffer)\n \t\treturn ERR_PTR(-ENOMEM);\n@@ -438,7 +441,8 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,\n \t\t\tgoto free_buffer;\n \t\t}\n \n-\t\tpage = alloc_largest_available(size_remaining, max_order);\n+\t\tpage = alloc_largest_available(size_remaining, max_order,\n+\t\t\t\t\t       cc_shared);\n \t\tif (!page)\n \t\t\tgoto free_buffer;\n \n@@ -454,19 +458,15 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,\n \n \tsg = table-\u003esgl;\n \tlist_for_each_entry_safe(page, tmp_page, \u0026pages, lru) {\n-\t\tsg_set_page(sg, page, page_size(page), 0);\n+\t\tunsigned long sg_len;\n+\n+\t\tsg_len = min_t(unsigned long, page_size(page), sg_remaining);\n+\t\tsg_set_page(sg, page, sg_len, 0);\n+\t\tsg_remaining -= sg_len;\n \t\tsg = sg_next(sg);\n \t\tlist_del(\u0026page-\u003elru);\n \t}\n \n-\tif (cc_shared_buffer(buffer)) {\n-\t\tfor_each_sgtable_sg(table, sg, i) {\n-\t\t\tret = system_heap_set_page_decrypted(sg_page(sg));\n-\t\t\tif (ret)\n-\t\t\t\tgoto free_pages;\n-\t\t}\n-\t}\n-\n \t/* create the dmabuf */\n \texp_info.exp_name = dma_heap_get_name(heap);\n \texp_info.ops = \u0026system_heap_buf_ops;\n@@ -481,22 +481,12 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,\n \treturn dmabuf;\n \n free_pages:\n-\tfor_each_sgtable_sg(table, sg, i) {\n-\t\tstruct page *p = sg_page(sg);\n-\n-\t\t/*\n-\t\t * Intentionally leak pages that cannot be re-encrypted\n-\t\t * to prevent shared memory from being reused.\n-\t\t */\n-\t\tif (cc_shared_buffer(buffer) \u0026\u0026\n-\t\t    system_heap_set_page_encrypted(p))\n-\t\t\tcontinue;\n-\t\t__free_pages(p, compound_order(p));\n-\t}\n+\tfor_each_sgtable_sg(table, sg, i)\n+\t\tsystem_heap_free_page(sg_page(sg), cc_shared);\n \tsg_free_table(table);\n free_buffer:\n \tlist_for_each_entry_safe(page, tmp_page, \u0026pages, lru)\n-\t\t__free_pages(page, compound_order(page));\n+\t\tsystem_heap_free_page(page, cc_shared);\n \tkfree(buffer);\n \n \treturn ERR_PTR(ret);\ndiff --git a/drivers/hv/connection.c b/drivers/hv/connection.c\nindex 1ab3581b096a4..3be779093f3c6 100644\n--- a/drivers/hv/connection.c\n+++ b/drivers/hv/connection.c\n@@ -13,6 +13,7 @@\n #include \u003clinux/sched.h\u003e\n #include \u003clinux/wait.h\u003e\n #include \u003clinux/delay.h\u003e\n+#include \u003clinux/cc_platform.h\u003e\n #include \u003clinux/mm.h\u003e\n #include \u003clinux/module.h\u003e\n #include \u003clinux/slab.h\u003e\n@@ -263,29 +264,27 @@ int vmbus_connect(void)\n \t\tgoto cleanup;\n \t}\n \n-\tret = set_memory_decrypted((unsigned long)\n-\t\t\t\tvmbus_connection.monitor_pages[0], 1);\n-\tret |= set_memory_decrypted((unsigned long)\n-\t\t\t\tvmbus_connection.monitor_pages[1], 1);\n-\tif (ret) {\n-\t\t/*\n-\t\t * If set_memory_decrypted() fails, the encryption state\n-\t\t * of the memory is unknown. So leak the memory instead\n-\t\t * of risking returning decrypted memory to the free list.\n-\t\t * For simplicity, always handle both pages the same.\n-\t\t */\n-\t\tvmbus_connection.monitor_pages[0] = NULL;\n-\t\tvmbus_connection.monitor_pages[1] = NULL;\n-\t\tgoto cleanup;\n+\tif (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) {\n+\t\tret = set_memory_decrypted((unsigned long)vmbus_connection.monitor_pages[0],\n+\t\t\t\t\t   1);\n+\t\tret |= set_memory_decrypted((unsigned long)vmbus_connection.monitor_pages[1],\n+\t\t\t\t\t    1);\n+\t\tif (ret) {\n+\t\t\t/*\n+\t\t\t * If set_memory_decrypted() fails, the encryption state\n+\t\t\t * of the memory is unknown. So leak the memory instead\n+\t\t\t * of risking returning decrypted memory to the free list.\n+\t\t\t * For simplicity, always handle both pages the same.\n+\t\t\t */\n+\t\t\tvmbus_connection.monitor_pages[0] = NULL;\n+\t\t\tvmbus_connection.monitor_pages[1] = NULL;\n+\t\t\tgoto cleanup;\n+\t\t}\n+\t} else {\n+\t\tmemset(vmbus_connection.monitor_pages[0], 0, HV_HYP_PAGE_SIZE);\n+\t\tmemset(vmbus_connection.monitor_pages[1], 0, HV_HYP_PAGE_SIZE);\n \t}\n \n-\t/*\n-\t * Set_memory_decrypted() will change the memory contents if\n-\t * decryption occurs, so zero monitor pages here.\n-\t */\n-\tmemset(vmbus_connection.monitor_pages[0], 0x00, HV_HYP_PAGE_SIZE);\n-\tmemset(vmbus_connection.monitor_pages[1], 0x00, HV_HYP_PAGE_SIZE);\n-\n \tmsginfo = kzalloc(sizeof(*msginfo) +\n \t\t\t  sizeof(struct vmbus_channel_initiate_contact),\n \t\t\t  GFP_KERNEL);\ndiff --git a/drivers/hv/hv.c b/drivers/hv/hv.c\nindex fe50090dcc011..6e836a02910a0 100644\n--- a/drivers/hv/hv.c\n+++ b/drivers/hv/hv.c\n@@ -123,12 +123,13 @@ static int hv_alloc_page(void **page, bool decrypt, const char *note)\n \tif (!*page)\n \t\treturn -ENOMEM;\n \n-\tif (decrypt)\n+\tif (decrypt) {\n \t\tret = set_memory_decrypted((unsigned long)*page, 1);\n-\tif (ret)\n-\t\tgoto failed;\n-\n-\tmemset(*page, 0, PAGE_SIZE);\n+\t\tif (ret)\n+\t\t\tgoto failed;\n+\t} else {\n+\t\tmemset(*page, 0, PAGE_SIZE);\n+\t}\n \treturn 0;\n \n failed:\ndiff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c\nindex 31256cb22b39e..af6b1531d6006 100644\n--- a/drivers/hv/hv_common.c\n+++ b/drivers/hv/hv_common.c\n@@ -505,8 +505,6 @@ int hv_common_cpu_init(unsigned int cpu)\n \t\t\t\t/* It may be unsafe to free 'mem' */\n \t\t\t\treturn ret;\n \t\t\t}\n-\n-\t\t\tmemset(mem, 0x00, pgcount * HV_HYP_PAGE_SIZE);\n \t\t}\n \n \t\t/*\ndiff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c\nindex 58c624513cd43..59baf2687612b 100644\n--- a/drivers/iommu/dma-iommu.c\n+++ b/drivers/iommu/dma-iommu.c\n@@ -1627,7 +1627,7 @@ static void *iommu_dma_alloc_pages(struct device *dev, size_t size,\n \tstruct page *page = NULL;\n \tvoid *cpu_addr;\n \n-\tpage = dma_alloc_contiguous(dev, alloc_size, gfp);\n+\tpage = dma_alloc_contiguous(dev, alloc_size, gfp, 0);\n \tif (!page)\n \t\tpage = alloc_pages_node(node, gfp, get_order(alloc_size));\n \tif (!page)\ndiff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c\nindex e9807af235373..a8f37fb978452 100644\n--- a/drivers/irqchip/irq-gic-v3-its.c\n+++ b/drivers/irqchip/irq-gic-v3-its.c\n@@ -8,6 +8,7 @@\n #include \u003clinux/acpi_iort.h\u003e\n #include \u003clinux/bitfield.h\u003e\n #include \u003clinux/bitmap.h\u003e\n+#include \u003clinux/cc_shared.h\u003e\n #include \u003clinux/cpu.h\u003e\n #include \u003clinux/crash_dump.h\u003e\n #include \u003clinux/delay.h\u003e\n@@ -19,7 +20,6 @@\n #include \u003clinux/irqdomain.h\u003e\n #include \u003clinux/list.h\u003e\n #include \u003clinux/log2.h\u003e\n-#include \u003clinux/mem_encrypt.h\u003e\n #include \u003clinux/memblock.h\u003e\n #include \u003clinux/mm.h\u003e\n #include \u003clinux/msi.h\u003e\n@@ -29,9 +29,9 @@\n #include \u003clinux/of_pci.h\u003e\n #include \u003clinux/of_platform.h\u003e\n #include \u003clinux/percpu.h\u003e\n-#include \u003clinux/set_memory.h\u003e\n #include \u003clinux/slab.h\u003e\n #include \u003clinux/syscore_ops.h\u003e\n+#include \u003clinux/topology.h\u003e\n \n #include \u003clinux/irqchip.h\u003e\n #include \u003clinux/irqchip/arm-gic-v3.h\u003e\n@@ -195,6 +195,7 @@ static DEFINE_PER_CPU(struct cpu_lpi_count, cpu_lpi_count);\n static LIST_HEAD(its_nodes);\n static DEFINE_RAW_SPINLOCK(its_lock);\n static struct rdists *gic_rdists;\n+static unsigned int vpe_l1_prealloc_order = UINT_MAX;\n static struct irq_domain *its_parent;\n \n static unsigned long its_list_map;\n@@ -213,41 +214,31 @@ static gfp_t gfp_flags_quirk;\n static struct page *its_alloc_pages_node(int node, gfp_t gfp,\n \t\t\t\t\t unsigned int order)\n {\n-\tstruct page *page;\n-\tint ret = 0;\n-\n-\tpage = alloc_pages_node(node, gfp | gfp_flags_quirk, order);\n-\n-\tif (!page)\n-\t\treturn NULL;\n+\tstruct cc_shared_pages mem;\n \n-\tret = set_memory_decrypted((unsigned long)page_address(page),\n-\t\t\t\t   1 \u003c\u003c order);\n-\t/*\n-\t * If set_memory_decrypted() fails then we don't know what state the\n-\t * page is in, so we can't free it. Instead we leak it.\n-\t * set_memory_decrypted() will already have WARNed.\n-\t */\n-\tif (ret)\n+\tif (alloc_cc_shared_pages_node(node, gfp | gfp_flags_quirk,\n+\t\t\t\t       PAGE_SIZE \u003c\u003c order, \u0026mem))\n \t\treturn NULL;\n \n-\treturn page;\n+\treturn mem.page;\n }\n \n static struct page *its_alloc_pages(gfp_t gfp, unsigned int order)\n {\n-\treturn its_alloc_pages_node(NUMA_NO_NODE, gfp, order);\n+\treturn its_alloc_pages_node(numa_mem_id(), gfp, order);\n }\n \n static void its_free_pages(void *addr, unsigned int order)\n {\n-\t/*\n-\t * If the memory cannot be encrypted again then we must leak the pages.\n-\t * set_memory_encrypted() will already have WARNed.\n-\t */\n-\tif (set_memory_encrypted((unsigned long)addr, 1 \u003c\u003c order))\n+\tstruct cc_shared_layout layout;\n+\tstruct cc_shared_pages mem;\n+\n+\tif (WARN_ON(cc_shared_calc_layout(PAGE_SIZE \u003c\u003c order, \u0026layout)))\n \t\treturn;\n-\tfree_pages((unsigned long)addr, order);\n+\n+\tmem.page = virt_to_page(addr);\n+\tmem.shared_size = layout.shared_size;\n+\tfree_cc_shared_pages(\u0026mem);\n }\n \n static struct gen_pool *itt_pool;\n@@ -272,7 +263,8 @@ static void *itt_alloc_pool(int node, int size)\n \t\tif (!page)\n \t\t\tbreak;\n \n-\t\tgen_pool_add(itt_pool, (unsigned long)page_address(page), PAGE_SIZE, node);\n+\t\tgen_pool_add(itt_pool, (unsigned long)page_address(page),\n+\t\t\t     cc_shared_granule_size(), node);\n \t} while (!addr);\n \n \treturn (void *)addr;\n@@ -2893,12 +2885,35 @@ static bool allocate_vpe_l2_table(int cpu, u32 id)\n \treturn true;\n }\n \n-static int allocate_vpe_l1_table(void)\n+static unsigned int vpe_l1_table_order(u64 val)\n+{\n+\tunsigned int psz;\n+\tu64 npg;\n+\n+\tswitch (FIELD_GET(GICR_VPROPBASER_4_1_PAGE_SIZE, val)) {\n+\tdefault:\n+\tcase GIC_PAGE_SIZE_4K:\n+\t\tpsz = SZ_4K;\n+\t\tbreak;\n+\tcase GIC_PAGE_SIZE_16K:\n+\t\tpsz = SZ_16K;\n+\t\tbreak;\n+\tcase GIC_PAGE_SIZE_64K:\n+\t\tpsz = SZ_64K;\n+\t\tbreak;\n+\t}\n+\n+\tnpg = FIELD_GET(GICR_VPROPBASER_4_1_SIZE, val) + 1;\n+\treturn get_order(npg * psz);\n+}\n+\n+static int allocate_vpe_l1_table(bool use_prealloc)\n {\n \tvoid __iomem *vlpi_base = gic_data_rdist_vlpi_base();\n \tu64 val, gpsz, npg, pa;\n \tunsigned int psz = SZ_64K;\n \tunsigned int np, epp, esz;\n+\tunsigned int order;\n \tstruct page *page;\n \n \tif (!gic_rdists-\u003ehas_rvpeid)\n@@ -2991,7 +3006,16 @@ static int allocate_vpe_l1_table(void)\n \n \tpr_debug(\"np = %d, npg = %lld, psz = %d, epp = %d, esz = %d\\n\",\n \t\t np, npg, psz, epp, esz);\n-\tpage = its_alloc_pages(GFP_ATOMIC | __GFP_ZERO, get_order(np * PAGE_SIZE));\n+\torder = get_order(np * PAGE_SIZE);\n+\tif (use_prealloc) {\n+\t\tif (WARN_ON_ONCE(order != vpe_l1_prealloc_order))\n+\t\t\treturn -EINVAL;\n+\n+\t\tpage = gic_data_rdist()-\u003evpe_l1_prealloc;\n+\t\tgic_data_rdist()-\u003evpe_l1_prealloc = NULL;\n+\t} else {\n+\t\tpage = its_alloc_pages(GFP_KERNEL | __GFP_ZERO, order);\n+\t}\n \tif (!page)\n \t\treturn -ENOMEM;\n \n@@ -3008,6 +3032,9 @@ static int allocate_vpe_l1_table(void)\n \tval |= GICR_VPROPBASER_4_1_VALID;\n \n out:\n+\tif (!use_prealloc)\n+\t\tvpe_l1_prealloc_order = vpe_l1_table_order(val);\n+\n \tgicr_write_vpropbaser(val, vlpi_base + GICR_VPROPBASER);\n \tcpumask_set_cpu(smp_processor_id(), gic_data_rdist()-\u003evpe_table_mask);\n \n@@ -3147,7 +3174,7 @@ static u64 its_clear_vpend_valid(void __iomem *vlpi_base, u64 clr, u64 set)\n \treturn val;\n }\n \n-static void its_cpu_init_lpis(void)\n+static void its_cpu_init_lpis(bool use_prealloc)\n {\n \tvoid __iomem *rbase = gic_data_rdist_rd_base();\n \tstruct page *pend_page;\n@@ -3260,7 +3287,7 @@ static void its_cpu_init_lpis(void)\n \t\tval = its_clear_vpend_valid(vlpi_base, 0, 0);\n \t}\n \n-\tif (allocate_vpe_l1_table()) {\n+\tif (allocate_vpe_l1_table(use_prealloc)) {\n \t\t/*\n \t\t * If the allocation has failed, we're in massive trouble.\n \t\t * Disable direct injection, and pray that no VM was\n@@ -5431,7 +5458,7 @@ static int redist_disable_lpis(void)\n \treturn 0;\n }\n \n-int its_cpu_init(void)\n+int its_cpu_init(bool use_prealloc)\n {\n \tif (!list_empty(\u0026its_nodes)) {\n \t\tint ret;\n@@ -5440,13 +5467,66 @@ int its_cpu_init(void)\n \t\tif (ret)\n \t\t\treturn ret;\n \n-\t\tits_cpu_init_lpis();\n+\t\tits_cpu_init_lpis(use_prealloc);\n \t\tits_cpu_init_collections();\n \t}\n \n \treturn 0;\n }\n \n+static int its_vpe_l1_prepare(unsigned int cpu)\n+{\n+\tstruct page **prealloc;\n+\n+\tif (!gic_rdists-\u003ehas_rvpeid ||\n+\t    vpe_l1_prealloc_order == UINT_MAX ||\n+\t    (gic_data_rdist_cpu(cpu)-\u003eflags \u0026 RD_LOCAL_LPI_ENABLED))\n+\t\treturn 0;\n+\n+\tprealloc = \u0026gic_data_rdist_cpu(cpu)-\u003evpe_l1_prealloc;\n+\tif (*prealloc)\n+\t\treturn 0;\n+\n+\t*prealloc = its_alloc_pages_node(cpu_to_node(cpu),\n+\t\t\t\t\t GFP_KERNEL | __GFP_ZERO,\n+\t\t\t\t\t vpe_l1_prealloc_order);\n+\treturn *prealloc ? 0 : -ENOMEM;\n+}\n+\n+static int its_vpe_l1_cleanup(unsigned int cpu)\n+{\n+\tstruct page *page;\n+\n+\tpage = xchg(\u0026gic_data_rdist_cpu(cpu)-\u003evpe_l1_prealloc, NULL);\n+\tif (page)\n+\t\tits_free_pages(page_address(page), vpe_l1_prealloc_order);\n+\n+\treturn 0;\n+}\n+\n+static int __init its_vpe_l1_cpuhp_init(void)\n+{\n+\tint prepare_state, state;\n+\n+\tstate = cpuhp_setup_state_nocalls(CPUHP_BP_PREPARE_DYN,\n+\t\t\t\t\t  \"irqchip/arm/gicv3-vpe:prepare\",\n+\t\t\t\t\t  its_vpe_l1_prepare,\n+\t\t\t\t\t  its_vpe_l1_cleanup);\n+\tif (state \u003c 0)\n+\t\treturn state;\n+\tprepare_state = state;\n+\n+\tstate = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,\n+\t\t\t\t\t  \"irqchip/arm/gicv3-vpe:online\",\n+\t\t\t\t\t  its_vpe_l1_cleanup, NULL);\n+\tif (state \u003c 0) {\n+\t\tcpuhp_remove_state_nocalls(prepare_state);\n+\t\treturn state;\n+\t}\n+\n+\treturn 0;\n+}\n+\n static void rdist_memreserve_cpuhp_cleanup_workfn(struct work_struct *work)\n {\n \tcpuhp_remove_state_nocalls(gic_rdists-\u003ecpuhp_memreserve_state);\n@@ -5871,6 +5951,15 @@ int __init its_init(struct fwnode_handle *handle, struct rdists *rdists,\n \tif (WARN_ON(!has_v4_1 \u0026\u0026 rdists-\u003ehas_rvpeid))\n \t\trdists-\u003ehas_rvpeid = false;\n \n+\tif (rdists-\u003ehas_rvpeid) {\n+\t\terr = its_vpe_l1_cpuhp_init();\n+\t\tif (err) {\n+\t\t\trdists-\u003ehas_rvpeid = false;\n+\t\t\trdists-\u003ehas_vlpis = false;\n+\t\t\tpr_err(\"ITS: Failed to prepare VPE tables, disabling GICv4 support\\n\");\n+\t\t}\n+\t}\n+\n \tif (has_v4 \u0026 rdists-\u003ehas_vlpis) {\n \t\tconst struct irq_domain_ops *sgi_ops;\n \ndiff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c\nindex 6e1fa5b247fc4..e2e00372dcfb0 100644\n--- a/drivers/irqchip/irq-gic-v3.c\n+++ b/drivers/irqchip/irq-gic-v3.c\n@@ -1320,7 +1320,7 @@ static int gic_starting_cpu(unsigned int cpu)\n \tgic_cpu_init();\n \n \tif (gic_dist_supports_lpis())\n-\t\tits_cpu_init();\n+\t\tits_cpu_init(true);\n \n \treturn 0;\n }\n@@ -2053,7 +2053,7 @@ static int __init gic_init_bases(phys_addr_t dist_phys_base,\n \n \tif (gic_dist_supports_lpis()) {\n \t\tits_init(handle, \u0026gic_data.rdists, gic_data.domain, dist_prio_irq);\n-\t\tits_cpu_init();\n+\t\tits_cpu_init(false);\n \t\tits_lpi_memreserve_init();\n \t} else {\n \t\tif (IS_ENABLED(CONFIG_ARM_GIC_V2M))\ndiff --git a/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c b/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c\nindex 26fe9c3f22e36..7d922e0a28f96 100644\n--- a/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c\n+++ b/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c\n@@ -13,6 +13,7 @@\n #include \u003clinux/mem_encrypt.h\u003e\n #include \u003clinux/mm.h\u003e\n #include \u003clinux/pgtable.h\u003e\n+#include \u003clinux/string.h\u003e\n \n #include \u003casm/hypervisor.h\u003e\n \n@@ -61,6 +62,8 @@ static int pkvm_set_memory_encrypted(unsigned long addr, int numpages)\n \n static int pkvm_set_memory_decrypted(unsigned long addr, int numpages)\n {\n+\tmemset((void *)addr, 0, (size_t)numpages \u003c\u003c PAGE_SHIFT);\n+\n \treturn __set_memory_range(ARM_SMCCC_VENDOR_HYP_KVM_MEM_SHARE_FUNC_ID,\n \t\t\t\t  addr, numpages);\n }\ndiff --git a/include/linux/cc_shared.h b/include/linux/cc_shared.h\nnew file mode 100644\nindex 0000000000000..5f8db7c468c5c\n--- /dev/null\n+++ b/include/linux/cc_shared.h\n@@ -0,0 +1,39 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+#ifndef _LINUX_CC_SHARED_H\n+#define _LINUX_CC_SHARED_H\n+\n+#include \u003clinux/gfp_types.h\u003e\n+#include \u003clinux/types.h\u003e\n+\n+struct page;\n+\n+struct cc_shared_pages {\n+\tstruct page *page;\n+\tsize_t shared_size;\n+};\n+\n+struct cc_shared_layout {\n+\tsize_t requested_size;\n+\tsize_t shared_size;\n+\tsize_t alignment;\n+};\n+\n+/*\n+ * Architectures may override this to return the granule used for transitions\n+ * between private and shared memory. The value must be a power of two and no\n+ * smaller than PAGE_SIZE.\n+ */\n+size_t arch_cc_shared_granule_size(void);\n+\n+size_t cc_shared_granule_size(void);\n+int cc_shared_calc_layout(size_t requested, struct cc_shared_layout *layout);\n+bool cc_shared_range_valid(phys_addr_t base, size_t size);\n+int cc_make_shared(void *addr, size_t size);\n+int cc_make_private(void *addr, size_t size);\n+int alloc_cc_shared_pages_node(int nid, gfp_t gfp,\n+\t\tsize_t requested, struct cc_shared_pages *mem);\n+int alloc_cc_shared_pages(gfp_t gfp,\n+\t\tsize_t requested, struct cc_shared_pages *mem);\n+void free_cc_shared_pages(struct cc_shared_pages *mem);\n+\n+#endif /* _LINUX_CC_SHARED_H */\ndiff --git a/include/linux/dma-map-ops.h b/include/linux/dma-map-ops.h\nindex 8fae2b7deb20d..f3490dd3f8ace 100644\n--- a/include/linux/dma-map-ops.h\n+++ b/include/linux/dma-map-ops.h\n@@ -99,10 +99,11 @@ int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base,\n \t\tphys_addr_t limit, struct cma **res_cma, bool fixed);\n \n struct page *dma_alloc_from_contiguous(struct device *dev, size_t count,\n-\t\t\t\t       unsigned int order, bool no_warn);\n+\t\tunsigned int order, unsigned long attrs, bool no_warn);\n bool dma_release_from_contiguous(struct device *dev, struct page *pages,\n \t\t\t\t int count);\n-struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp);\n+struct page *dma_alloc_contiguous(struct device *dev, size_t size,\n+\t\t\t\t  gfp_t gfp, unsigned long attrs);\n void dma_free_contiguous(struct device *dev, struct page *page, size_t size);\n \n void dma_contiguous_early_fixup(phys_addr_t base, unsigned long size);\n@@ -125,7 +126,7 @@ static inline int dma_contiguous_reserve_area(phys_addr_t size,\n \treturn -ENOSYS;\n }\n static inline struct page *dma_alloc_from_contiguous(struct device *dev,\n-\t\tsize_t count, unsigned int order, bool no_warn)\n+\t\tsize_t count, unsigned int order, unsigned long attrs, bool no_warn)\n {\n \treturn NULL;\n }\n@@ -136,7 +137,7 @@ static inline bool dma_release_from_contiguous(struct device *dev,\n }\n /* Use fallback alloc() and free() when CONFIG_DMA_CMA=n */\n static inline struct page *dma_alloc_contiguous(struct device *dev, size_t size,\n-\t\tgfp_t gfp)\n+\t\tgfp_t gfp, unsigned long attrs)\n {\n \treturn NULL;\n }\ndiff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h\nindex ea5fd2374ebe0..a5565a7451009 100644\n--- a/include/linux/irqchip/arm-gic-v3.h\n+++ b/include/linux/irqchip/arm-gic-v3.h\n@@ -621,6 +621,7 @@ struct rdists {\n \t\tu64             flags;\n \t\tcpumask_t\t*vpe_table_mask;\n \t\tvoid\t\t*vpe_l1_base;\n+\t\tstruct page\t*vpe_l1_prealloc;\n \t} __percpu\t\t*rdist;\n \tphys_addr_t\t\tprop_table_pa;\n \tvoid\t\t\t*prop_table_va;\n@@ -637,7 +638,7 @@ struct rdists {\n struct irq_domain;\n struct fwnode_handle;\n int __init its_lpi_memreserve_init(void);\n-int its_cpu_init(void);\n+int its_cpu_init(bool use_prealloc);\n int its_init(struct fwnode_handle *handle, struct rdists *rdists,\n \t     struct irq_domain *domain, u8 irq_prio);\n int mbi_init(struct fwnode_handle *fwnode, struct irq_domain *parent);\ndiff --git a/kernel/dma/contiguous.c b/kernel/dma/contiguous.c\nindex 66093460584e8..1a29565c91858 100644\n--- a/kernel/dma/contiguous.c\n+++ b/kernel/dma/contiguous.c\n@@ -39,6 +39,7 @@\n \n #include \u003casm/page.h\u003e\n \n+#include \u003clinux/cc_shared.h\u003e\n #include \u003clinux/memblock.h\u003e\n #include \u003clinux/err.h\u003e\n #include \u003clinux/sizes.h\u003e\n@@ -357,19 +358,29 @@ int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base,\n  * dma_alloc_from_contiguous() - allocate pages from contiguous area\n  * @dev:   Pointer to device for which the allocation is performed.\n  * @count: Requested number of pages.\n- * @align: Requested alignment of pages (in PAGE_SIZE order).\n+ * @align: Preferred alignment of pages (in PAGE_SIZE order).\n+ * @attrs: DMA allocation attributes.\n  * @no_warn: Avoid printing message about failed allocation.\n  *\n  * This function allocates memory buffer for specified device. It uses\n  * device specific contiguous memory area if available or the default\n  * global one. Requires architecture specific dev_get_cma_area() helper\n  * function.\n+ *\n+ * The preferred alignment is capped at CONFIG_CMA_ALIGNMENT. The internal\n+ * shared-allocation attribute requires at least the architecture shared\n+ * granule alignment and fails if that exceeds the CMA alignment limit.\n  */\n struct page *dma_alloc_from_contiguous(struct device *dev, size_t count,\n-\t\t\t\t       unsigned int align, bool no_warn)\n+\t\tunsigned int align, unsigned long attrs, bool no_warn)\n {\n-\tif (align \u003e CONFIG_CMA_ALIGNMENT)\n-\t\talign = CONFIG_CMA_ALIGNMENT;\n+\tunsigned int required_align = 0;\n+\n+\tif (attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED)\n+\t\trequired_align = get_order(cc_shared_granule_size());\n+\tif (required_align \u003e CONFIG_CMA_ALIGNMENT)\n+\t\treturn NULL;\n+\talign = min(max(align, required_align), CONFIG_CMA_ALIGNMENT);\n \n \treturn cma_alloc(dev_get_cma_area(dev), count, align, no_warn);\n }\n@@ -390,10 +401,9 @@ bool dma_release_from_contiguous(struct device *dev, struct page *pages,\n \treturn cma_release(dev_get_cma_area(dev), pages, count);\n }\n \n-static struct page *cma_alloc_aligned(struct cma *cma, size_t size, gfp_t gfp)\n+static struct page *cma_alloc_aligned(struct cma *cma, size_t size, gfp_t gfp,\n+\t\t\t\t      unsigned int align)\n {\n-\tunsigned int align = min(get_order(size), CONFIG_CMA_ALIGNMENT);\n-\n \treturn cma_alloc(cma, size \u003e\u003e PAGE_SHIFT, align, gfp \u0026 __GFP_NOWARN);\n }\n \n@@ -402,6 +412,7 @@ static struct page *cma_alloc_aligned(struct cma *cma, size_t size, gfp_t gfp)\n  * @dev:   Pointer to device for which the allocation is performed.\n  * @size:  Requested allocation size.\n  * @gfp:   Allocation flags.\n+ * @attrs: DMA allocation attributes.\n  *\n  * tries to use device specific contiguous memory area if available, or it\n  * tries to use per-numa cma, if the allocation fails, it will fallback to\n@@ -412,8 +423,11 @@ static struct page *cma_alloc_aligned(struct cma *cma, size_t size, gfp_t gfp)\n  * there is no need to waste CMA pages for that kind; it also helps reduce\n  * fragmentations.\n  */\n-struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp)\n+struct page *dma_alloc_contiguous(struct device *dev, size_t size,\n+\t\t\t\t  gfp_t gfp, unsigned long attrs)\n {\n+\tunsigned int required_align = 0;\n+\tunsigned int align = get_order(size);\n #ifdef CONFIG_DMA_NUMA_CMA\n \tint nid = dev_to_node(dev);\n #endif\n@@ -421,8 +435,13 @@ struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp)\n \t/* CMA can be used only in the context which permits sleeping */\n \tif (!gfpflags_allow_blocking(gfp))\n \t\treturn NULL;\n+\tif (attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED)\n+\t\trequired_align = get_order(cc_shared_granule_size());\n+\tif (required_align \u003e CONFIG_CMA_ALIGNMENT)\n+\t\treturn NULL;\n+\talign = min(max(align, required_align), CONFIG_CMA_ALIGNMENT);\n \tif (dev-\u003ecma_area)\n-\t\treturn cma_alloc_aligned(dev-\u003ecma_area, size, gfp);\n+\t\treturn cma_alloc_aligned(dev-\u003ecma_area, size, gfp, align);\n \tif (size \u003c= PAGE_SIZE)\n \t\treturn NULL;\n \n@@ -431,7 +450,7 @@ struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp)\n \t\tstruct cma *cma = dma_contiguous_numa_area[nid];\n \t\tstruct page *page;\n \t\tif (cma) {\n-\t\t\tpage = cma_alloc_aligned(cma, size, gfp);\n+\t\t\tpage = cma_alloc_aligned(cma, size, gfp, align);\n \t\t\tif (page)\n \t\t\t\treturn page;\n \t\t}\n@@ -440,7 +459,7 @@ struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp)\n \tif (!dma_contiguous_default_area)\n \t\treturn NULL;\n \n-\treturn cma_alloc_aligned(dma_contiguous_default_area, size, gfp);\n+\treturn cma_alloc_aligned(dma_contiguous_default_area, size, gfp, align);\n }\n \n /**\ndiff --git a/kernel/dma/direct.c b/kernel/dma/direct.c\nindex da665ca22d5c0..09a8a842fc6fc 100644\n--- a/kernel/dma/direct.c\n+++ b/kernel/dma/direct.c\n@@ -11,10 +11,10 @@\n #include \u003clinux/scatterlist.h\u003e\n #include \u003clinux/pfn.h\u003e\n #include \u003clinux/vmalloc.h\u003e\n-#include \u003clinux/set_memory.h\u003e\n #include \u003clinux/slab.h\u003e\n #include \u003clinux/pci-p2pdma.h\u003e\n #include \u003clinux/cc_platform.h\u003e\n+#include \u003clinux/cc_shared.h\u003e\n \n #include \"direct.h\"\n \n@@ -85,7 +85,7 @@ static int dma_set_decrypted(struct device *dev, void *vaddr, size_t size)\n {\n \tint ret;\n \n-\tret = set_memory_decrypted((unsigned long)vaddr, PFN_UP(size));\n+\tret = cc_make_shared(vaddr, size);\n \tif (ret)\n \t\tpr_warn_ratelimited(\"leaking DMA memory that can't be decrypted\\n\");\n \treturn ret;\n@@ -95,7 +95,7 @@ static int dma_set_encrypted(struct device *dev, void *vaddr, size_t size)\n {\n \tint ret;\n \n-\tret = set_memory_encrypted((unsigned long)vaddr, PFN_UP(size));\n+\tret = cc_make_private(vaddr, size);\n \tif (ret)\n \t\tpr_warn_ratelimited(\"leaking DMA memory that can't be re-encrypted\\n\");\n \treturn ret;\n@@ -115,7 +115,7 @@ static struct page *dma_direct_alloc_swiotlb(struct device *dev, size_t size,\n }\n \n static struct page *__dma_direct_alloc_pages(struct device *dev, size_t size,\n-\t\tgfp_t gfp, bool allow_highmem)\n+\t\tgfp_t gfp, bool allow_highmem, unsigned long attrs)\n {\n \tint node = dev_to_node(dev);\n \tstruct page *page;\n@@ -124,7 +124,7 @@ static struct page *__dma_direct_alloc_pages(struct device *dev, size_t size,\n \tWARN_ON_ONCE(!PAGE_ALIGNED(size));\n \n \tgfp |= dma_direct_optimal_gfp_mask(dev, \u0026phys_limit);\n-\tpage = dma_alloc_contiguous(dev, size, gfp);\n+\tpage = dma_alloc_contiguous(dev, size, gfp, attrs);\n \tif (page) {\n \t\tif (dma_coherent_ok(dev, page_to_phys(page), size) \u0026\u0026\n \t\t    (allow_highmem || !PageHighMem(page)))\n@@ -184,7 +184,7 @@ static void *dma_direct_alloc_no_mapping(struct device *dev, size_t size,\n {\n \tstruct page *page;\n \n-\tpage = __dma_direct_alloc_pages(dev, size, gfp \u0026 ~__GFP_ZERO, true);\n+\tpage = __dma_direct_alloc_pages(dev, size, gfp \u0026 ~__GFP_ZERO, true, 0);\n \tif (!page)\n \t\treturn NULL;\n \n@@ -205,13 +205,15 @@ void *dma_direct_alloc(struct device *dev, size_t size,\n \tbool remap = false, set_uncached = false;\n \tbool mark_mem_decrypt = false;\n \tbool allow_highmem = true;\n+\tstruct cc_shared_layout layout;\n \tstruct page *page;\n \tvoid *cpu_addr;\n \n \tif (force_dma_unencrypted(dev))\n \t\tattrs |= __DMA_ATTR_ALLOC_CC_SHARED;\n \n-\tif (attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED) {\n+\tmark_mem_decrypt = attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED;\n+\tif (mark_mem_decrypt) {\n \t\t/*\n \t\t * Unencrypted/shared DMA requires a linear-mapped buffer\n \t\t * address to look up the PFN and set architecture-required PFN\n@@ -219,7 +221,6 @@ void *dma_direct_alloc(struct device *dev, size_t size,\n \t\t * allocation.\n \t\t */\n \t\tallow_highmem = false;\n-\t\tmark_mem_decrypt = true;\n \t}\n \n \tsize = PAGE_ALIGN(size);\n@@ -285,8 +286,15 @@ void *dma_direct_alloc(struct device *dev, size_t size,\n \t\treturn NULL;\n \t}\n \n+\tif (mark_mem_decrypt) {\n+\t\tif (cc_shared_calc_layout(size, \u0026layout))\n+\t\t\treturn NULL;\n+\t\tsize = layout.shared_size;\n+\t}\n+\n \t/* we always manually zero the memory once we are done */\n-\tpage = __dma_direct_alloc_pages(dev, size, gfp \u0026 ~__GFP_ZERO, allow_highmem);\n+\tpage = __dma_direct_alloc_pages(dev, size, gfp \u0026 ~__GFP_ZERO,\n+\t\t\t\t\tallow_highmem, attrs);\n \tif (!page)\n \t\treturn NULL;\n \n@@ -305,7 +313,7 @@ void *dma_direct_alloc(struct device *dev, size_t size,\n \t\tvoid *lm_addr;\n \n \t\tlm_addr = page_address(page);\n-\t\tif (set_memory_decrypted((unsigned long)lm_addr, PFN_UP(size)))\n+\t\tif (dma_set_decrypted(dev, lm_addr, size))\n \t\t\tgoto out_leak_pages;\n \t}\n \n@@ -324,7 +332,9 @@ void *dma_direct_alloc(struct device *dev, size_t size,\n \t\tcpu_addr = page_address(page);\n \t}\n \n-\tmemset(cpu_addr, 0, size);\n+\t/* Zero after remapping because the page may be in HighMem. */\n+\tif (!mark_mem_decrypt)\n+\t\tmemset(cpu_addr, 0, size);\n \n \tif (set_uncached) {\n \t\tvoid *uncached_cpu_addr;\n@@ -362,6 +372,7 @@ void dma_direct_free(struct device *dev, size_t size,\n \tphys_addr_t phys;\n \tbool mark_mem_encrypted = false;\n \tstruct io_tlb_pool *swiotlb_pool;\n+\tstruct cc_shared_layout layout;\n \tunsigned int page_order = get_order(size);\n \n \t/*\n@@ -406,6 +417,12 @@ void dma_direct_free(struct device *dev, size_t size,\n \t\t/* Swiotlb doesn't need a page attribute update on free */\n \t\tmark_mem_encrypted = false;\n \n+\tif (mark_mem_encrypted) {\n+\t\tif (WARN_ON_ONCE(cc_shared_calc_layout(size, \u0026layout)))\n+\t\t\treturn;\n+\t\tsize = layout.shared_size;\n+\t}\n+\n \tif (is_vmalloc_addr(cpu_addr)) {\n \t\tvunmap(cpu_addr);\n \t} else {\n@@ -417,10 +434,8 @@ void dma_direct_free(struct device *dev, size_t size,\n \t\tvoid *lm_addr;\n \n \t\tlm_addr = phys_to_virt(phys);\n-\t\tif (set_memory_encrypted((unsigned long)lm_addr, PFN_UP(size))) {\n-\t\t\tpr_warn_ratelimited(\"leaking DMA memory that can't be re-encrypted\\n\");\n+\t\tif (dma_set_encrypted(dev, lm_addr, size))\n \t\t\treturn;\n-\t\t}\n \t}\n \n \tif (swiotlb_pool)\n@@ -433,12 +448,16 @@ struct page *dma_direct_alloc_pages(struct device *dev, size_t size,\n \t\tdma_addr_t *dma_handle, enum dma_data_direction dir, gfp_t gfp)\n {\n \tunsigned long attrs = 0;\n+\tstruct cc_shared_layout layout;\n \tstruct page *page;\n \tvoid *cpu_addr;\n+\tbool mark_mem_decrypt;\n \n \tif (force_dma_unencrypted(dev))\n \t\tattrs |= __DMA_ATTR_ALLOC_CC_SHARED;\n \n+\tmark_mem_decrypt = attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED;\n+\n \tif ((attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED) \u0026\u0026 dma_direct_use_pool(dev, gfp))\n \t\treturn dma_direct_alloc_from_pool(dev, size, dma_handle,\n \t\t\t\t\t\t  \u0026cpu_addr, gfp, attrs);\n@@ -449,19 +468,28 @@ struct page *dma_direct_alloc_pages(struct device *dev, size_t size,\n \t\t\treturn NULL;\n \n \t\tcpu_addr = page_address(page);\n+\t\tmark_mem_decrypt = false;\n \t\tgoto setup_page;\n \t}\n \n-\tpage = __dma_direct_alloc_pages(dev, size, gfp, false);\n+\tif (mark_mem_decrypt) {\n+\t\tif (cc_shared_calc_layout(size, \u0026layout))\n+\t\t\treturn NULL;\n+\t\tsize = layout.shared_size;\n+\t}\n+\n+\tpage = __dma_direct_alloc_pages(dev, size, gfp, false, attrs);\n \tif (!page)\n \t\treturn NULL;\n \n \tcpu_addr = page_address(page);\n-\tif ((attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED) \u0026\u0026\n-\t    dma_set_decrypted(dev, cpu_addr, size))\n-\t\tgoto out_leak_pages;\n setup_page:\n-\tmemset(cpu_addr, 0, size);\n+\tif (mark_mem_decrypt) {\n+\t\tif (dma_set_decrypted(dev, cpu_addr, size))\n+\t\t\tgoto out_leak_pages;\n+\t} else {\n+\t\tmemset(cpu_addr, 0, size);\n+\t}\n \t*dma_handle = phys_to_dma_direct(dev, page_to_phys(page),\n \t\t\t\t\t attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED);\n \treturn page;\n@@ -476,6 +504,7 @@ void dma_direct_free_pages(struct device *dev, size_t size,\n \tphys_addr_t phys;\n \tvoid *vaddr = page_address(page);\n \tstruct io_tlb_pool *swiotlb_pool;\n+\tstruct cc_shared_layout layout;\n \t/*\n \t * if the device had requested for an unencrypted buffer,\n \t * convert it to encrypted on free\n@@ -492,6 +521,12 @@ void dma_direct_free_pages(struct device *dev, size_t size,\n \tif (swiotlb_pool)\n \t\tmark_mem_encrypted = false;\n \n+\tif (mark_mem_encrypted) {\n+\t\tif (WARN_ON_ONCE(cc_shared_calc_layout(size, \u0026layout)))\n+\t\t\treturn;\n+\t\tsize = layout.shared_size;\n+\t}\n+\n \tif (mark_mem_encrypted \u0026\u0026 dma_set_encrypted(dev, vaddr, size))\n \t\treturn;\n \ndiff --git a/kernel/dma/ops_helpers.c b/kernel/dma/ops_helpers.c\nindex 6b5f9208d31c0..8320cc0fada50 100644\n--- a/kernel/dma/ops_helpers.c\n+++ b/kernel/dma/ops_helpers.c\n@@ -66,7 +66,7 @@ struct page *dma_common_alloc_pages(struct device *dev, size_t size,\n \tstruct page *page;\n \tphys_addr_t phys;\n \n-\tpage = dma_alloc_contiguous(dev, size, gfp);\n+\tpage = dma_alloc_contiguous(dev, size, gfp, 0);\n \tif (!page)\n \t\tpage = alloc_pages_node(dev_to_node(dev), gfp, get_order(size));\n \tif (!page)\ndiff --git a/kernel/dma/pool.c b/kernel/dma/pool.c\nindex 00f422a1e8966..f059312c15aa5 100644\n--- a/kernel/dma/pool.c\n+++ b/kernel/dma/pool.c\n@@ -4,12 +4,12 @@\n  * Copyright (C) 2020 Google LLC\n  */\n #include \u003clinux/cma.h\u003e\n+#include \u003clinux/cc_shared.h\u003e\n #include \u003clinux/debugfs.h\u003e\n #include \u003clinux/dma-map-ops.h\u003e\n #include \u003clinux/dma-direct.h\u003e\n #include \u003clinux/init.h\u003e\n #include \u003clinux/genalloc.h\u003e\n-#include \u003clinux/set_memory.h\u003e\n #include \u003clinux/slab.h\u003e\n #include \u003clinux/workqueue.h\u003e\n #include \u003clinux/cc_platform.h\u003e\n@@ -85,6 +85,9 @@ static bool cma_in_zone(gfp_t gfp)\n static int atomic_pool_expand(struct dma_gen_pool *dma_pool, size_t pool_size,\n \t\t\t      gfp_t gfp)\n {\n+\tstruct cc_shared_layout layout;\n+\tunsigned long attrs = 0;\n+\tunsigned int min_order = 0;\n \tunsigned int order;\n \tstruct page *page = NULL;\n \tbool leak_pages = false;\n@@ -92,6 +95,17 @@ static int atomic_pool_expand(struct dma_gen_pool *dma_pool, size_t pool_size,\n \tint ret = -ENOMEM;\n \tpgprot_t prot __maybe_unused;\n \n+\tif (dma_pool-\u003ecc_shared) {\n+\t\tret = cc_shared_calc_layout(pool_size, \u0026layout);\n+\t\tif (ret)\n+\t\t\tgoto out;\n+\t\tpool_size = layout.shared_size;\n+\t\tmin_order = get_order(layout.alignment);\n+\t\tif (min_order \u003e MAX_PAGE_ORDER)\n+\t\t\treturn -E2BIG;\n+\t\tattrs = __DMA_ATTR_ALLOC_CC_SHARED;\n+\t}\n+\n \t/* Cannot allocate larger than MAX_PAGE_ORDER */\n \torder = min(get_order(pool_size), MAX_PAGE_ORDER);\n \n@@ -99,10 +113,10 @@ static int atomic_pool_expand(struct dma_gen_pool *dma_pool, size_t pool_size,\n \t\tpool_size = 1 \u003c\u003c (PAGE_SHIFT + order);\n \t\tif (cma_in_zone(gfp))\n \t\t\tpage = dma_alloc_from_contiguous(NULL, 1 \u003c\u003c order,\n-\t\t\t\t\t\t\t order, false);\n+\t\t\t\t\t\t\t order, attrs, false);\n \t\tif (!page)\n \t\t\tpage = alloc_pages(gfp | __GFP_NOWARN, order);\n-\t} while (!page \u0026\u0026 order-- \u003e 0);\n+\t} while (!page \u0026\u0026 order-- \u003e min_order);\n \tif (!page)\n \t\tgoto out;\n \n@@ -126,8 +140,7 @@ static int atomic_pool_expand(struct dma_gen_pool *dma_pool, size_t pool_size,\n \t * shrink so no re-encryption occurs in dma_direct_free().\n \t */\n \tif (dma_pool-\u003ecc_shared) {\n-\t\tret = set_memory_decrypted((unsigned long)page_to_virt(page),\n-\t\t\t\t\t   1 \u003c\u003c order);\n+\t\tret = cc_make_shared(page_to_virt(page), pool_size);\n \t\tif (ret) {\n \t\t\tleak_pages = true;\n \t\t\tgoto remove_mapping;\n@@ -144,7 +157,7 @@ static int atomic_pool_expand(struct dma_gen_pool *dma_pool, size_t pool_size,\n \n encrypt_mapping:\n \tif (dma_pool-\u003ecc_shared \u0026\u0026\n-\t    set_memory_encrypted((unsigned long)page_to_virt(page), 1 \u003c\u003c order))\n+\t    cc_make_private(page_to_virt(page), pool_size))\n \t\tleak_pages = true;\n \n remove_mapping:\ndiff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c\nindex ded7016a46a71..8d71f2e0fd855 100644\n--- a/kernel/dma/swiotlb.c\n+++ b/kernel/dma/swiotlb.c\n@@ -22,6 +22,7 @@\n \n #include \u003clinux/cache.h\u003e\n #include \u003clinux/cc_platform.h\u003e\n+#include \u003clinux/cc_shared.h\u003e\n #include \u003clinux/ctype.h\u003e\n #include \u003clinux/debugfs.h\u003e\n #include \u003clinux/dma-direct.h\u003e\n@@ -38,7 +39,6 @@\n #include \u003clinux/pfn.h\u003e\n #include \u003clinux/rculist.h\u003e\n #include \u003clinux/scatterlist.h\u003e\n-#include \u003clinux/set_memory.h\u003e\n #include \u003clinux/spinlock.h\u003e\n #include \u003clinux/string.h\u003e\n #include \u003clinux/swiotlb.h\u003e\n@@ -330,6 +330,14 @@ static inline unsigned long nr_slots(u64 val)\n \treturn DIV_ROUND_UP(val, IO_TLB_SIZE);\n }\n \n+static unsigned long swiotlb_align_nslabs(unsigned long nslabs)\n+{\n+\tunsigned long granule_nslabs;\n+\n+\tgranule_nslabs = cc_shared_granule_size() \u003e\u003e IO_TLB_SHIFT;\n+\treturn ALIGN(nslabs, granule_nslabs);\n+}\n+\n static void swiotlb_mark_pool_used(struct io_tlb_pool *pool)\n {\n \tunsigned long i;\n@@ -369,13 +377,13 @@ void __init swiotlb_update_mem_attributes(void)\n \n \tif (!mem-\u003enslabs || mem-\u003elate_alloc)\n \t\treturn;\n-\tbytes = PAGE_ALIGN(mem-\u003enslabs \u003c\u003c IO_TLB_SHIFT);\n+\n+\tbytes = ALIGN(mem-\u003enslabs \u003c\u003c IO_TLB_SHIFT, cc_shared_granule_size());\n \n \tif (io_tlb_default_mem.cc_shared) {\n \t\tint ret;\n \n-\t\tret = set_memory_decrypted((unsigned long)mem-\u003evaddr,\n-\t\t\t\t\t   bytes \u003e\u003e PAGE_SHIFT);\n+\t\tret = cc_make_shared(mem-\u003evaddr, bytes);\n \t\tif (ret) {\n \t\t\tpr_warn(\"Failed to decrypt default memory pool, disabling it\\n\");\n \t\t\tswiotlb_mark_pool_used(mem);\n@@ -433,10 +441,11 @@ static void add_mem_pool(struct io_tlb_mem *mem, struct io_tlb_pool *pool)\n }\n \n static void __init *swiotlb_memblock_alloc(unsigned long nslabs,\n-\t\tunsigned int flags,\n+\t\tunsigned long *alloc_nslabs, unsigned int flags,\n \t\tint (*remap)(void *tlb, unsigned long nslabs))\n {\n-\tsize_t bytes = PAGE_ALIGN(nslabs \u003c\u003c IO_TLB_SHIFT);\n+\tunsigned long aligned_nslabs = swiotlb_align_nslabs(nslabs);\n+\tsize_t bytes = aligned_nslabs \u003c\u003c IO_TLB_SHIFT;\n \tvoid *tlb;\n \n \t/*\n@@ -445,9 +454,9 @@ static void __init *swiotlb_memblock_alloc(unsigned long nslabs,\n \t * memory encryption.\n \t */\n \tif (flags \u0026 SWIOTLB_ANY)\n-\t\ttlb = memblock_alloc(bytes, PAGE_SIZE);\n+\t\ttlb = memblock_alloc(bytes, cc_shared_granule_size());\n \telse\n-\t\ttlb = memblock_alloc_low(bytes, PAGE_SIZE);\n+\t\ttlb = memblock_alloc_low(bytes, cc_shared_granule_size());\n \n \tif (!tlb) {\n \t\tpr_warn(\"%s: Failed to allocate %zu bytes tlb structure\\n\",\n@@ -455,12 +464,13 @@ static void __init *swiotlb_memblock_alloc(unsigned long nslabs,\n \t\treturn NULL;\n \t}\n \n-\tif (remap \u0026\u0026 remap(tlb, nslabs) \u003c 0) {\n-\t\tmemblock_free(tlb, PAGE_ALIGN(bytes));\n+\tif (remap \u0026\u0026 remap(tlb, aligned_nslabs) \u003c 0) {\n+\t\tmemblock_free(tlb, bytes);\n \t\tpr_warn(\"%s: Failed to remap %zu bytes\\n\", __func__, bytes);\n \t\treturn NULL;\n \t}\n \n+\t*alloc_nslabs = aligned_nslabs;\n \treturn tlb;\n }\n \n@@ -473,6 +483,7 @@ void __init swiotlb_init_remap(bool addressing_limit, unsigned int flags,\n {\n \tstruct io_tlb_pool *mem = \u0026io_tlb_default_mem.defpool;\n \tunsigned long nslabs;\n+\tunsigned long alloc_nslabs;\n \tunsigned int nareas;\n \tsize_t alloc_size;\n \tvoid *tlb;\n@@ -497,13 +508,14 @@ void __init swiotlb_init_remap(bool addressing_limit, unsigned int flags,\n \t\tswiotlb_adjust_nareas(num_possible_cpus());\n \n \tnslabs = default_nslabs;\n-\tnareas = limit_nareas(default_nareas, nslabs);\n-\twhile ((tlb = swiotlb_memblock_alloc(nslabs, flags, remap)) == NULL) {\n+\twhile ((tlb = swiotlb_memblock_alloc(nslabs, \u0026alloc_nslabs, flags,\n+\t\t\t\t\t     remap)) == NULL) {\n \t\tif (nslabs \u003c= IO_TLB_MIN_SLABS)\n \t\t\treturn;\n \t\tnslabs = ALIGN(nslabs \u003e\u003e 1, IO_TLB_SEGSIZE);\n-\t\tnareas = limit_nareas(nareas, nslabs);\n \t}\n+\tnslabs = alloc_nslabs;\n+\tnareas = limit_nareas(default_nareas, nslabs);\n \n \tif (default_nslabs != nslabs) {\n \t\tpr_info(\"SWIOTLB bounce buffer size adjusted %lu -\u003e %lu slabs\",\n@@ -578,7 +590,7 @@ int swiotlb_init_late(size_t size, gfp_t gfp_mask,\n \t\tswiotlb_adjust_nareas(num_possible_cpus());\n \n retry:\n-\torder = get_order(nslabs \u003c\u003c IO_TLB_SHIFT);\n+\torder = get_order(ALIGN(nslabs \u003c\u003c IO_TLB_SHIFT, cc_shared_granule_size()));\n \tnslabs = SLABS_PER_PAGE \u003c\u003c order;\n \n \twhile ((SLABS_PER_PAGE \u003c\u003c order) \u003e IO_TLB_MIN_SLABS) {\n@@ -587,6 +599,8 @@ int swiotlb_init_late(size_t size, gfp_t gfp_mask,\n \t\tif (vstart)\n \t\t\tbreak;\n \t\torder--;\n+\t\tif (order \u003c get_order(cc_shared_granule_size()))\n+\t\t\tbreak;\n \t\tnslabs = SLABS_PER_PAGE \u003c\u003c order;\n \t\tretried = true;\n \t}\n@@ -626,8 +640,7 @@ int swiotlb_init_late(size_t size, gfp_t gfp_mask,\n \t\tgoto error_slots;\n \n \tif (io_tlb_default_mem.cc_shared) {\n-\t\trc = set_memory_decrypted((unsigned long)vstart,\n-\t\t\t\t\t  (nslabs \u003c\u003c IO_TLB_SHIFT) \u003e\u003e PAGE_SHIFT);\n+\t\trc = cc_make_shared(vstart, nslabs \u003c\u003c IO_TLB_SHIFT);\n \t\tif (rc) {\n \t\t\tleak_pages = true;\n \t\t\tgoto error_decrypt;\n@@ -667,11 +680,11 @@ void __init swiotlb_exit(void)\n \n \tpr_info(\"tearing down default memory pool\\n\");\n \ttbl_vaddr = (unsigned long)phys_to_virt(mem-\u003estart);\n-\ttbl_size = PAGE_ALIGN(mem-\u003eend - mem-\u003estart);\n+\ttbl_size = ALIGN(mem-\u003eend - mem-\u003estart, cc_shared_granule_size());\n \tslots_size = PAGE_ALIGN(array_size(sizeof(*mem-\u003eslots), mem-\u003enslabs));\n \n \tif (io_tlb_default_mem.cc_shared) {\n-\t\tif (set_memory_encrypted(tbl_vaddr, tbl_size \u003e\u003e PAGE_SHIFT))\n+\t\tif (cc_make_private((void *)tbl_vaddr, tbl_size))\n \t\t\tleak_pages = true;\n \t}\n \n@@ -711,12 +724,15 @@ void __init swiotlb_exit(void)\n static struct page *alloc_dma_pages(gfp_t gfp, size_t bytes,\n \t\tu64 phys_limit, unsigned long attrs)\n {\n-\tunsigned int order = get_order(bytes);\n \tbool cc_shared = attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED;\n+\tunsigned int order;\n \tstruct page *page;\n \tphys_addr_t paddr;\n \tvoid *vaddr;\n \n+\tif (cc_shared)\n+\t\tbytes = ALIGN(bytes, cc_shared_granule_size());\n+\torder = get_order(bytes);\n \tpage = alloc_pages(gfp, order);\n \tif (!page)\n \t\treturn NULL;\n@@ -728,13 +744,13 @@ static struct page *alloc_dma_pages(gfp_t gfp, size_t bytes,\n \t}\n \n \tvaddr = phys_to_virt(paddr);\n-\tif (cc_shared \u0026\u0026 set_memory_decrypted((unsigned long)vaddr, PFN_UP(bytes)))\n+\tif (cc_shared \u0026\u0026 cc_make_shared(vaddr, bytes))\n \t\tgoto error;\n \treturn page;\n \n error:\n \t/* Intentional leak if pages cannot be encrypted again. */\n-\tif (cc_shared \u0026\u0026 !set_memory_encrypted((unsigned long)vaddr, PFN_UP(bytes)))\n+\tif (cc_shared \u0026\u0026 !cc_make_private(vaddr, bytes))\n \t\t__free_pages(page, order);\n \treturn NULL;\n }\n@@ -807,9 +823,11 @@ static void swiotlb_free_tlb(void *vaddr, size_t bytes, bool cc_shared)\n \t    dma_free_from_pool(NULL, vaddr, bytes))\n \t\treturn;\n \n+\tif (cc_shared)\n+\t\tbytes = ALIGN(bytes, cc_shared_granule_size());\n \t/* Intentional leak if pages cannot be encrypted again. */\n \tif (!cc_shared ||\n-\t    !set_memory_encrypted((unsigned long)vaddr, PFN_UP(bytes)))\n+\t    !cc_make_private(vaddr, bytes))\n \t\t__free_pages(virt_to_page(vaddr), get_order(bytes));\n }\n \n@@ -863,6 +881,12 @@ static struct io_tlb_pool *swiotlb_alloc_pool(struct device *dev,\n \t\ttlb_size = nslabs \u003c\u003c IO_TLB_SHIFT;\n \t}\n \n+\t/* Transient pools are tied to one mapping and cannot reuse padding. */\n+\tif (mem-\u003ecc_shared \u0026\u0026 !dev) {\n+\t\tnslabs = swiotlb_align_nslabs(nslabs);\n+\t\ttlb_size = nslabs \u003c\u003c IO_TLB_SHIFT;\n+\t}\n+\n \tslot_order = get_order(array_size(sizeof(*pool-\u003eslots), nslabs));\n \tpool-\u003eslots = (struct io_tlb_slot *)\n \t\t__get_free_pages(gfp, slot_order);\n@@ -2002,6 +2026,14 @@ static int rmem_swiotlb_device_init(struct reserved_mem *rmem,\n \t\treturn -EINVAL;\n \t}\n \n+\tif (cc_platform_has(CC_ATTR_MEM_ENCRYPT) \u0026\u0026\n+\t    !cc_shared_range_valid(rmem-\u003ebase, rmem-\u003esize)) {\n+\t\tdev_err(dev,\n+\t\t\t\"Restricted DMA pool must be aligned to %#zx bytes for memory encryption\\n\",\n+\t\t\tcc_shared_granule_size());\n+\t\treturn -EINVAL;\n+\t}\n+\n \t/*\n \t * Since multiple devices can share the same pool, the private data,\n \t * io_tlb_mem struct, will be initialized by the first device attached\n@@ -2035,8 +2067,8 @@ static int rmem_swiotlb_device_init(struct reserved_mem *rmem,\n \t\t\tint ret;\n \n \t\t\tmem-\u003ecc_shared = true;\n-\t\t\tret = set_memory_decrypted((unsigned long)phys_to_virt(rmem-\u003ebase),\n-\t\t\t\t\t\t   rmem-\u003esize \u003e\u003e PAGE_SHIFT);\n+\t\t\tret = cc_make_shared(phys_to_virt(rmem-\u003ebase),\n+\t\t\t\t\t     rmem-\u003esize);\n \t\t\tif (ret) {\n \t\t\t\tdev_err(dev, \"Failed to decrypt restricted DMA pool\\n\");\n \t\t\t\tkfree(pool-\u003eareas);\ndiff --git a/kernel/kexec_file.c b/kernel/kexec_file.c\nindex 59fb9d71e9d86..2a337ce7264e4 100644\n--- a/kernel/kexec_file.c\n+++ b/kernel/kexec_file.c\n@@ -682,7 +682,8 @@ static int kexec_alloc_contig(struct kexec_buf *kbuf)\n \tif (kbuf-\u003eimage-\u003etype == KEXEC_TYPE_CRASH)\n \t\treturn -EPERM;\n \n-\tp = dma_alloc_from_contiguous(NULL, nr_pages, get_order(kbuf-\u003ebuf_align), true);\n+\tp = dma_alloc_from_contiguous(NULL, nr_pages,\n+\t\t\t\t      get_order(kbuf-\u003ebuf_align), 0, true);\n \tif (!p)\n \t\treturn -ENOMEM;\n \ndiff --git a/mm/Makefile b/mm/Makefile\nindex e7245cb88c665..6e65444284226 100644\n--- a/mm/Makefile\n+++ b/mm/Makefile\n@@ -56,6 +56,7 @@ obj-y\t\t\t:= filemap.o mempool.o oom_kill.o fadvise.o \\\n \t\t\t   compaction.o show_mem.o \\\n \t\t\t   interval_tree.o list_lru.o workingset.o \\\n \t\t\t   debug.o gup.o mmap_lock.o vma_init.o $(mmu-y)\n+obj-y\t\t\t+= cc_shared.o\n \n # Give 'page_alloc' its own module-parameter namespace\n page-alloc-y := page_alloc.o\ndiff --git a/mm/cc_shared.c b/mm/cc_shared.c\nnew file mode 100644\nindex 0000000000000..6f68a3f44ef80\n--- /dev/null\n+++ b/mm/cc_shared.c\n@@ -0,0 +1,232 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/*\n+ * Copyright (C) 2026 ARM Ltd.\n+ */\n+#include \u003clinux/align.h\u003e\n+#include \u003clinux/cc_platform.h\u003e\n+#include \u003clinux/cc_shared.h\u003e\n+#include \u003clinux/errno.h\u003e\n+#include \u003clinux/export.h\u003e\n+#include \u003clinux/gfp.h\u003e\n+#include \u003clinux/kernel.h\u003e\n+#include \u003clinux/log2.h\u003e\n+#include \u003clinux/mm.h\u003e\n+#include \u003clinux/mem_encrypt.h\u003e\n+#include \u003clinux/numa.h\u003e\n+#include \u003clinux/overflow.h\u003e\n+#include \u003clinux/set_memory.h\u003e\n+\n+size_t __weak arch_cc_shared_granule_size(void)\n+{\n+\treturn PAGE_SIZE;\n+}\n+\n+size_t cc_shared_granule_size(void)\n+{\n+\tsize_t granule = arch_cc_shared_granule_size();\n+\n+\tif (WARN_ON_ONCE(granule \u003c PAGE_SIZE || !is_power_of_2(granule)))\n+\t\treturn PAGE_SIZE;\n+\n+\treturn granule;\n+}\n+EXPORT_SYMBOL_GPL(cc_shared_granule_size);\n+\n+int cc_shared_calc_layout(size_t requested, struct cc_shared_layout *layout)\n+{\n+\tsize_t granule, rounded;\n+\n+\tif (!requested || !layout)\n+\t\treturn -EINVAL;\n+\n+\tgranule = cc_shared_granule_size();\n+\tif (check_add_overflow(requested, granule - 1, \u0026rounded))\n+\t\treturn -EOVERFLOW;\n+\n+\trounded = ALIGN_DOWN(rounded, granule);\n+\tlayout-\u003erequested_size = requested;\n+\tlayout-\u003eshared_size = rounded;\n+\tlayout-\u003ealignment = granule;\n+\n+\treturn 0;\n+}\n+EXPORT_SYMBOL_GPL(cc_shared_calc_layout);\n+\n+bool cc_shared_range_valid(phys_addr_t base, size_t size)\n+{\n+\tsize_t granule = cc_shared_granule_size();\n+\n+\tif (!size)\n+\t\treturn false;\n+\n+\treturn IS_ALIGNED(base, granule) \u0026\u0026 IS_ALIGNED(size, granule);\n+}\n+EXPORT_SYMBOL_GPL(cc_shared_range_valid);\n+\n+static int cc_validate_transition(void *addr, size_t size)\n+{\n+\tphys_addr_t phys;\n+\n+\tif (!addr || !size || !PAGE_ALIGNED(addr) ||\n+\t    !virt_addr_valid(addr))\n+\t\treturn -EINVAL;\n+\n+\tphys = page_to_phys(virt_to_page(addr));\n+\tif (!cc_shared_range_valid(phys, size))\n+\t\treturn -EINVAL;\n+\n+\treturn 0;\n+}\n+\n+int cc_make_shared(void *addr, size_t size)\n+{\n+\tint ret = cc_validate_transition(addr, size);\n+\n+\tif (ret)\n+\t\treturn ret;\n+\n+\treturn set_memory_decrypted((unsigned long)addr, size \u003e\u003e PAGE_SHIFT);\n+}\n+\n+int cc_make_private(void *addr, size_t size)\n+{\n+\tint ret = cc_validate_transition(addr, size);\n+\n+\tif (ret)\n+\t\treturn ret;\n+\n+\treturn set_memory_encrypted((unsigned long)addr, size \u003e\u003e PAGE_SHIFT);\n+}\n+\n+static int __alloc_cc_shared_pages_node(int nid, gfp_t gfp,\n+\t\t\t\t\tsize_t requested,\n+\t\t\t\t\tstruct cc_shared_pages *mem)\n+{\n+\tstruct cc_shared_layout layout;\n+\tstruct page *page;\n+\tunsigned int order;\n+\tint ret;\n+\n+\tmight_sleep();\n+\n+\tret = cc_shared_calc_layout(requested, \u0026layout);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\torder = get_order(layout.shared_size);\n+\tif (order \u003e MAX_PAGE_ORDER)\n+\t\treturn -EINVAL;\n+\n+\t/*\n+\t * State transitions require a linear-map address and may modify memory.\n+\t * Allocate from low memory and let the architecture place zeroing at the\n+\t * appropriate point in the transition.\n+\t */\n+\tgfp \u0026= ~(__GFP_HIGHMEM | __GFP_ZERO);\n+\tif (nid == NUMA_NO_NODE)\n+\t\tpage = alloc_pages(gfp, order);\n+\telse\n+\t\tpage = alloc_pages_node(nid, gfp, order);\n+\tif (!page)\n+\t\treturn -ENOMEM;\n+\n+\tret = cc_make_shared(page_address(page), layout.shared_size);\n+\tif (ret) {\n+\t\tif (!cc_make_private(page_address(page), layout.shared_size))\n+\t\t\t__free_pages(page, order);\n+\t\telse\n+\t\t\tpr_warn_ratelimited(\"leaking %zu bytes with uncertain shared state\\n\",\n+\t\t\t\t\t    layout.shared_size);\n+\t\treturn ret;\n+\t}\n+\n+\tmem-\u003epage = page;\n+\tmem-\u003eshared_size = layout.shared_size;\n+\treturn 0;\n+}\n+\n+/**\n+ * alloc_cc_shared_pages_node - allocate memory that can be shared\n+ * @nid: NUMA node from which to allocate, or %NUMA_NO_NODE\n+ * @gfp: allocation flags\n+ * @requested: number of bytes requested; must be nonzero\n+ * @mem: storage for the allocated page and the size of the shared range\n+ *\n+ * Allocate at least @requested bytes and make the allocation shared when\n+ * memory encryption is active.  A memory-state transition requires a valid\n+ * linear-map address, so such allocations never come from high memory\n+ *\n+ * The shared range may be rounded up to the architecture's transition\n+ * granule.  On success, @mem-\u003eshared_size records the actual size that was\n+ * made shared and must be retained unchanged for free_cc_shared_pages().\n+ * @mem is not modified on failure.\n+ *\n+ * Return: 0 on success, or a negative error code on failure.\n+ */\n+int alloc_cc_shared_pages_node(int nid, gfp_t gfp,\n+\t\t\t       size_t requested,\n+\t\t\t       struct cc_shared_pages *mem)\n+{\n+\tstruct page *page;\n+\tunsigned int order;\n+\n+\tif (!mem || !requested)\n+\t\treturn -EINVAL;\n+\n+\tif (cc_platform_has(CC_ATTR_MEM_ENCRYPT))\n+\t\treturn __alloc_cc_shared_pages_node(nid, gfp, requested, mem);\n+\n+\torder = get_order(requested);\n+\tif (order \u003e MAX_PAGE_ORDER)\n+\t\treturn -EINVAL;\n+\n+\tif (nid == NUMA_NO_NODE)\n+\t\tpage = alloc_pages(gfp, order);\n+\telse\n+\t\tpage = alloc_pages_node(nid, gfp, order);\n+\tif (!page)\n+\t\treturn -ENOMEM;\n+\n+\tmem-\u003epage = page;\n+\tmem-\u003eshared_size = requested;\n+\treturn 0;\n+}\n+EXPORT_SYMBOL_GPL(alloc_cc_shared_pages_node);\n+\n+/**\n+ * alloc_cc_shared_pages - allocate memory that can be shared\n+ * @gfp: allocation flags\n+ * @requested: number of bytes requested; must be nonzero\n+ * @mem: storage for the allocated page and the size of the shared range\n+ *\n+ * Equivalent to alloc_cc_shared_pages_node() with %NUMA_NO_NODE.\n+ *\n+ * Return: 0 on success, or a negative error code on failure.\n+ */\n+int alloc_cc_shared_pages(gfp_t gfp,\n+\t\t\t  size_t requested, struct cc_shared_pages *mem)\n+{\n+\treturn alloc_cc_shared_pages_node(NUMA_NO_NODE, gfp, requested, mem);\n+}\n+EXPORT_SYMBOL_GPL(alloc_cc_shared_pages);\n+\n+void free_cc_shared_pages(struct cc_shared_pages *mem)\n+{\n+\tif (!mem || !mem-\u003epage)\n+\t\treturn;\n+\n+\tif (!cc_platform_has(CC_ATTR_MEM_ENCRYPT))\n+\t\tgoto free_pages;\n+\n+\tif (cc_make_private(page_address(mem-\u003epage), mem-\u003eshared_size)) {\n+\t\tpr_warn_ratelimited(\"leaking %zu bytes that cannot be made private\\n\",\n+\t\t\t\t    mem-\u003eshared_size);\n+\t\treturn;\n+\t}\n+\n+free_pages:\n+\t__free_pages(mem-\u003epage, get_order(mem-\u003eshared_size));\n+\tmem-\u003epage = NULL;\n+\tmem-\u003eshared_size = 0;\n+}\n+EXPORT_SYMBOL_GPL(free_cc_shared_pages);\n"
}

2/1 2026/09/26 15:28 agent patch-evaluator
3m Model:
core

Args:
null

Results:
{
  "EnableConfigs": [
    "DMABUF_HEAPS",
    "DMABUF_HEAPS_SYSTEM",
    "DMABUF_HEAPS_SYSTEM_CC_SHARED",
    "DMA_CMA",
    "CMA",
    "AMD_MEM_ENCRYPT"
  ],
  "FocusSymbols": [
    "system_heap_allocate",
    "system_heap_dma_buf_release",
    "dma_alloc_contiguous",
    "dma_alloc_from_contiguous",
    "atomic_pool_expand"
  ],
  "Reasoning": "The patch series introduces shared page allocation and alignment helpers for confidential computing, while refactoring memory allocation and release paths across the DMA contiguous memory allocator (CMA), atomic DMA pools, and the DMA-buf system heap (drivers/dma-buf/heaps/system_heap.c). These changes modify reachable code paths on amd64, including user-accessible dma_heap allocation/release logic and CMA buffer allocation.",
  "WorthFuzzing": true
}

Instruction:
You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.

================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.

- REACHABILITY IS THE PRIMARY GATE:
  Fuzzing can only discover bugs in code that can actually execute in standard virtualized
  environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
  If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
  regardless of whether it adds assertions or complex logic.

- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
  Patch authors routinely label changes as "cleanups", "refactorings", or state
  "No functional change intended". Do NOT take these claims at face value.
  Code refactorings that rearrange logic, introduce helper functions, or alter state management
  in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
  If reachable executable code is modified or refactored, it MUST be fuzzed.

- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
  When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
  BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
  Even if the author believes the invariant always holds, fuzzing is essential to verify whether
  an unusual sequence of operations can violate it.

================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:

- Non-kernel and non-executable changes:
  * Modifications to Documentation/, comments, or spelling fixes.
  * User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
    that do not affect the compiled kernel image (vmlinux) or kernel modules.
  * Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
    that do not alter control flow or data structures.
  * Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
  * Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
    ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
  * Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
    physical PCI hot-unplug or manual sysfs driver unbinding.
  * Code paths exclusive to architectures other than the target architecture.

================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
  * Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
    BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
  * Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
  * Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
  * Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).

================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:

- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
  Do NOT list generic, ubiquitous functions called by almost every program in the corpus
  (including, but not limited to: general memory allocators and deallocators, page fault
  and trap handlers, or core synchronization primitives; this is not an exhaustive list).
  Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
  which severely dilutes fuzzing effort away from the actual changes.

- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
  List functions that specifically implement the logic being added or altered, or direct API entrypoints
  for the subsystem feature under review.

- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
  Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
  distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
  If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
  functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).

================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit 4a46b87645d47c6cdb58e9df76e3a9445bb4efbd
Author: syz-cluster <triage@syzkaller.com>
Date:   Sat Sep 26 15:28:34 2026 +0000

    syz-cluster: applied patch under review

diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index 7761099dde9e8..9714fcd519418 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -398,7 +398,8 @@ static void *__alloc_from_contiguous(struct device *dev, size_t size,
 	struct page *page;
 	void *ptr = NULL;
 
-	page = dma_alloc_from_contiguous(dev, count, order, gfp & __GFP_NOWARN);
+	page = dma_alloc_from_contiguous(dev, count, order, 0,
+					 gfp & __GFP_NOWARN);
 	if (!page)
 		return NULL;
 
@@ -866,7 +867,7 @@ static struct page **__iommu_alloc_buffer(struct device *dev, size_t size,
 		unsigned long order = get_order(size);
 		struct page *page;
 
-		page = dma_alloc_from_contiguous(dev, count, order,
+		page = dma_alloc_from_contiguous(dev, count, order, 0,
 						 gfp & __GFP_NOWARN);
 		if (!page)
 			goto error;
diff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c
index bbe98ac9ad8c6..ae1f5de66fad7 100644
--- a/arch/arm64/mm/pageattr.c
+++ b/arch/arm64/mm/pageattr.c
@@ -339,6 +339,9 @@ static int realm_set_memory_decrypted(unsigned long addr, int numpages)
 {
 	int ret = __set_memory_enc_dec(addr, numpages, false);
 
+	if (!ret)
+		memset((void *)addr, 0, (size_t)numpages << PAGE_SHIFT);
+
 	WARN(ret, "Failed to decrypt memory, %d pages will be leaked",
 	     numpages);
 
diff --git a/arch/powerpc/platforms/pseries/svm.c b/arch/powerpc/platforms/pseries/svm.c
index 7a403dbd35ee5..a344e094fe1c8 100644
--- a/arch/powerpc/platforms/pseries/svm.c
+++ b/arch/powerpc/platforms/pseries/svm.c
@@ -9,6 +9,7 @@
 #include <linux/mm.h>
 #include <linux/memblock.h>
 #include <linux/mem_encrypt.h>
+#include <linux/string.h>
 #include <linux/cc_platform.h>
 #include <asm/machdep.h>
 #include <asm/svm.h>
@@ -59,6 +60,7 @@ int set_memory_decrypted(unsigned long addr, int numpages)
 	if (!PAGE_ALIGNED(addr))
 		return -EINVAL;
 
+	memset((void *)addr, 0, (size_t)numpages << PAGE_SHIFT);
 	uv_share_page(PHYS_PFN(__pa(addr)), numpages);
 
 	return 0;
diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c
index be7e009e7b59e..f2e28a2710e04 100644
--- a/arch/s390/mm/init.c
+++ b/arch/s390/mm/init.c
@@ -129,6 +129,9 @@ int set_memory_encrypted(unsigned long vaddr, int numpages)
 int set_memory_decrypted(unsigned long vaddr, int numpages)
 {
 	int i;
+
+	memset((void *)vaddr, 0, (size_t)numpages << PAGE_SHIFT);
+
 	/* make specified pages shared (swiotlb, dma_alloca) */
 	for (i = 0; i < numpages; ++i) {
 		uv_set_shared(virt_to_phys((void *)vaddr));
diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c
index f904a636d449b..1f1f390823911 100644
--- a/arch/x86/coco/tdx/tdx.c
+++ b/arch/x86/coco/tdx/tdx.c
@@ -978,6 +978,9 @@ static bool tdx_enc_status_changed(unsigned long vaddr, int numpages, bool enc)
 static int tdx_enc_status_change_prepare(unsigned long vaddr, int numpages,
 					 bool enc)
 {
+	if (!enc)
+		memset((void *)vaddr, 0, (size_t)numpages << PAGE_SHIFT);
+
 	/*
 	 * Only handle shared->private conversion here.
 	 * See the comment in tdx_early_init().
diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c
index 0b4a1c0b0b16f..a42468e81c344 100644
--- a/arch/x86/hyperv/hv_init.c
+++ b/arch/x86/hyperv/hv_init.c
@@ -156,8 +156,10 @@ static int hv_cpu_init(unsigned int cpu)
 			 * page in non-root partition here.
 			 */
 			if (*hvp && !ms_hyperv.paravisor_present && hv_isolation_type_snp()) {
-				WARN_ON_ONCE(set_memory_decrypted((unsigned long)(*hvp), 1));
-				memset(*hvp, 0, PAGE_SIZE);
+				int ret;
+
+				ret = set_memory_decrypted((unsigned long)*hvp, 1);
+				WARN_ON_ONCE(ret);
 			}
 		}
 
diff --git a/arch/x86/hyperv/ivm.c b/arch/x86/hyperv/ivm.c
index 2ce4dfe534727..e9c7799a00943 100644
--- a/arch/x86/hyperv/ivm.c
+++ b/arch/x86/hyperv/ivm.c
@@ -755,6 +755,10 @@ static int hv_mark_gpa_visibility(u16 count, const u64 pfn[],
  */
 static int hv_vtom_clear_present(unsigned long kbuffer, int pagecount, bool enc)
 {
+	if (!enc)
+		memset((void *)kbuffer, 0,
+		       (size_t)pagecount << PAGE_SHIFT);
+
 	return set_memory_np(kbuffer, pagecount);
 }
 
diff --git a/arch/x86/kernel/kvmclock.c b/arch/x86/kernel/kvmclock.c
index cb3d0ca1fa225..3e9b7ad3dea2b 100644
--- a/arch/x86/kernel/kvmclock.c
+++ b/arch/x86/kernel/kvmclock.c
@@ -248,7 +248,7 @@ static void __init kvmclock_init_mem(void)
 	 * be mapped decrypted.
 	 */
 	if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) {
-		r = set_memory_decrypted((unsigned long) hvclock_mem,
+		r = set_memory_decrypted((unsigned long)hvclock_mem,
 					 1UL << order);
 		if (r) {
 			__free_pages(p, order);
@@ -256,9 +256,9 @@ static void __init kvmclock_init_mem(void)
 			pr_warn("kvmclock: set_memory_decrypted() failed. Disabling\n");
 			return;
 		}
+	} else {
+		memset(hvclock_mem, 0, PAGE_SIZE << order);
 	}
-
-	memset(hvclock_mem, 0, PAGE_SIZE << order);
 }
 
 static int __init kvm_setup_vsyscall_timeinfo(void)
diff --git a/arch/x86/mm/mem_encrypt_amd.c b/arch/x86/mm/mem_encrypt_amd.c
index 2f8c32173972d..47cae102acd24 100644
--- a/arch/x86/mm/mem_encrypt_amd.c
+++ b/arch/x86/mm/mem_encrypt_amd.c
@@ -13,6 +13,7 @@
 #include <linux/dma-direct.h>
 #include <linux/swiotlb.h>
 #include <linux/mem_encrypt.h>
+#include <linux/string.h>
 #include <linux/device.h>
 #include <linux/kernel.h>
 #include <linux/bitops.h>
@@ -285,6 +286,9 @@ static void enc_dec_hypercall(unsigned long vaddr, unsigned long size, bool enc)
 
 static int amd_enc_status_change_prepare(unsigned long vaddr, int npages, bool enc)
 {
+	if (!enc)
+		memset((void *)vaddr, 0, (size_t)npages << PAGE_SHIFT);
+
 	/*
 	 * To maintain the security guarantees of SEV-SNP guests, make sure
 	 * to invalidate the memory before encryption attribute is cleared.
diff --git a/drivers/dma-buf/heaps/system_heap.c b/drivers/dma-buf/heaps/system_heap.c
index c8959eadc71d1..5970b76416ae5 100644
--- a/drivers/dma-buf/heaps/system_heap.c
+++ b/drivers/dma-buf/heaps/system_heap.c
@@ -11,14 +11,13 @@
  */
 
 #include <linux/cc_platform.h>
+#include <linux/cc_shared.h>
 #include <linux/dma-buf.h>
 #include <linux/dma-mapping.h>
 #include <linux/dma-heap.h>
 #include <linux/err.h>
 #include <linux/highmem.h>
-#include <linux/mem_encrypt.h>
 #include <linux/mm.h>
-#include <linux/set_memory.h>
 #include <linux/module.h>
 #include <linux/pgtable.h>
 #include <linux/scatterlist.h>
@@ -65,34 +64,6 @@ static gfp_t order_flags[] = {HIGH_ORDER_GFP, HIGH_ORDER_GFP, LOW_ORDER_GFP};
 static const unsigned int orders[] = {8, 4, 0};
 #define NUM_ORDERS ARRAY_SIZE(orders)
 
-static int system_heap_set_page_decrypted(struct page *page)
-{
-	unsigned long addr = (unsigned long)page_address(page);
-	unsigned int nr_pages = 1 << compound_order(page);
-	int ret;
-
-	ret = set_memory_decrypted(addr, nr_pages);
-	if (ret)
-		pr_warn_ratelimited("dma-buf system heap: failed to decrypt page at %p\n",
-				    page_address(page));
-
-	return ret;
-}
-
-static int system_heap_set_page_encrypted(struct page *page)
-{
-	unsigned long addr = (unsigned long)page_address(page);
-	unsigned int nr_pages = 1 << compound_order(page);
-	int ret;
-
-	ret = set_memory_encrypted(addr, nr_pages);
-	if (ret)
-		pr_warn_ratelimited("dma-buf system heap: failed to re-encrypt page at %p, leaking memory\n",
-				    page_address(page));
-
-	return ret;
-}
-
 static int dup_sg_table(struct sg_table *from, struct sg_table *to)
 {
 	struct scatterlist *sg, *new_sg;
@@ -337,6 +308,20 @@ static void system_heap_vunmap(struct dma_buf *dmabuf, struct iosys_map *map)
 	iosys_map_clear(map);
 }
 
+static void system_heap_free_page(struct page *page, bool cc_shared)
+{
+	struct cc_shared_pages mem;
+
+	if (!cc_shared) {
+		__free_pages(page, compound_order(page));
+		return;
+	}
+
+	mem.page = page;
+	mem.shared_size = page_size(page);
+	free_cc_shared_pages(&mem);
+}
+
 static void system_heap_dma_buf_release(struct dma_buf *dmabuf)
 {
 	struct system_heap_buffer *buffer = dmabuf->priv;
@@ -345,19 +330,8 @@ static void system_heap_dma_buf_release(struct dma_buf *dmabuf)
 	int i;
 
 	table = &buffer->sg_table;
-	for_each_sgtable_sg(table, sg, i) {
-		struct page *page = sg_page(sg);
-
-		/*
-		 * Intentionally leak pages that cannot be re-encrypted
-		 * to prevent shared memory from being reused.
-		 */
-		if (cc_shared_buffer(buffer) &&
-		    system_heap_set_page_encrypted(page))
-			continue;
-
-		__free_pages(page, compound_order(page));
-	}
+	for_each_sgtable_sg(table, sg, i)
+		system_heap_free_page(sg_page(sg), cc_shared_buffer(buffer));
 	sg_free_table(table);
 	kfree(buffer);
 }
@@ -375,22 +349,39 @@ static const struct dma_buf_ops system_heap_buf_ops = {
 	.release = system_heap_dma_buf_release,
 };
 
+static struct page *system_heap_alloc_order(unsigned int order,
+	     gfp_t flags, bool cc_shared)
+{
+	struct cc_shared_pages mem;
+
+	if (!cc_shared)
+		return alloc_pages(flags, order);
+
+	/* The shared granule can raise the actual allocation order. */
+	flags |= __GFP_COMP;
+	if (alloc_cc_shared_pages(flags, PAGE_SIZE << order, &mem))
+		return NULL;
+
+	return mem.page;
+}
+
 static struct page *alloc_largest_available(unsigned long size,
-					    unsigned int max_order)
+		unsigned int max_order, bool cc_shared)
 {
 	struct page *page;
-	int i;
 	gfp_t flags;
+	int i;
 
 	for (i = 0; i < NUM_ORDERS; i++) {
 		if (size <  (PAGE_SIZE << orders[i]))
 			continue;
 		if (max_order < orders[i])
 			continue;
+
 		flags = order_flags[i];
 		if (mem_accounting)
 			flags |= __GFP_ACCOUNT;
-		page = alloc_pages(flags, orders[i]);
+		page = system_heap_alloc_order(orders[i], flags, cc_shared);
 		if (!page)
 			continue;
 		return page;
@@ -405,7 +396,9 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
 {
 	struct system_heap_buffer *buffer;
 	DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+	struct cc_shared_layout layout;
 	unsigned long size_remaining = len;
+	unsigned long sg_remaining = len;
 	unsigned int max_order = orders[0];
 	struct system_heap_priv *priv = dma_heap_get_drvdata(heap);
 	bool cc_shared = priv->cc_shared;
@@ -416,6 +409,16 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
 	struct page *page, *tmp_page;
 	int i, ret = -ENOMEM;
 
+	if (cc_shared) {
+		int err;
+
+		err = cc_shared_calc_layout(len, &layout);
+		if (err)
+			return ERR_PTR(err);
+
+		size_remaining = layout.shared_size;
+	}
+
 	buffer = kzalloc_obj(*buffer);
 	if (!buffer)
 		return ERR_PTR(-ENOMEM);
@@ -438,7 +441,8 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
 			goto free_buffer;
 		}
 
-		page = alloc_largest_available(size_remaining, max_order);
+		page = alloc_largest_available(size_remaining, max_order,
+					       cc_shared);
 		if (!page)
 			goto free_buffer;
 
@@ -454,19 +458,15 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
 
 	sg = table->sgl;
 	list_for_each_entry_safe(page, tmp_page, &pages, lru) {
-		sg_set_page(sg, page, page_size(page), 0);
+		unsigned long sg_len;
+
+		sg_len = min_t(unsigned long, page_size(page), sg_remaining);
+		sg_set_page(sg, page, sg_len, 0);
+		sg_remaining -= sg_len;
 		sg = sg_next(sg);
 		list_del(&page->lru);
 	}
 
-	if (cc_shared_buffer(buffer)) {
-		for_each_sgtable_sg(table, sg, i) {
-			ret = system_heap_set_page_decrypted(sg_page(sg));
-			if (ret)
-				goto free_pages;
-		}
-	}
-
 	/* create the dmabuf */
 	exp_info.exp_name = dma_heap_get_name(heap);
 	exp_info.ops = &system_heap_buf_ops;
@@ -481,22 +481,12 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
 	return dmabuf;
 
 free_pages:
-	for_each_sgtable_sg(table, sg, i) {
-		struct page *p = sg_page(sg);
-
-		/*
-		 * Intentionally leak pages that cannot be re-encrypted
-		 * to prevent shared memory from being reused.
-		 */
-		if (cc_shared_buffer(buffer) &&
-		    system_heap_set_page_encrypted(p))
-			continue;
-		__free_pages(p, compound_order(p));
-	}
+	for_each_sgtable_sg(table, sg, i)
+		system_heap_free_page(sg_page(sg), cc_shared);
 	sg_free_table(table);
 free_buffer:
 	list_for_each_entry_safe(page, tmp_page, &pages, lru)
-		__free_pages(page, compound_order(page));
+		system_heap_free_page(page, cc_shared);
 	kfree(buffer);
 
 	return ERR_PTR(ret);
diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c
index 1ab3581b096a4..3be779093f3c6 100644
--- a/drivers/hv/connection.c
+++ b/drivers/hv/connection.c
@@ -13,6 +13,7 @@
 #include <linux/sched.h>
 #include <linux/wait.h>
 #include <linux/delay.h>
+#include <linux/cc_platform.h>
 #include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/slab.h>
@@ -263,29 +264,27 @@ int vmbus_connect(void)
 		goto cleanup;
 	}
 
-	ret = set_memory_decrypted((unsigned long)
-				vmbus_connection.monitor_pages[0], 1);
-	ret |= set_memory_decrypted((unsigned long)
-				vmbus_connection.monitor_pages[1], 1);
-	if (ret) {
-		/*
-		 * If set_memory_decrypted() fails, the encryption state
-		 * of the memory is unknown. So leak the memory instead
-		 * of risking returning decrypted memory to the free list.
-		 * For simplicity, always handle both pages the same.
-		 */
-		vmbus_connection.monitor_pages[0] = NULL;
-		vmbus_connection.monitor_pages[1] = NULL;
-		goto cleanup;
+	if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) {
+		ret = set_memory_decrypted((unsigned long)vmbus_connection.monitor_pages[0],
+					   1);
+		ret |= set_memory_decrypted((unsigned long)vmbus_connection.monitor_pages[1],
+					    1);
+		if (ret) {
+			/*
+			 * If set_memory_decrypted() fails, the encryption state
+			 * of the memory is unknown. So leak the memory instead
+			 * of risking returning decrypted memory to the free list.
+			 * For simplicity, always handle both pages the same.
+			 */
+			vmbus_connection.monitor_pages[0] = NULL;
+			vmbus_connection.monitor_pages[1] = NULL;
+			goto cleanup;
+		}
+	} else {
+		memset(vmbus_connection.monitor_pages[0], 0, HV_HYP_PAGE_SIZE);
+		memset(vmbus_connection.monitor_pages[1], 0, HV_HYP_PAGE_SIZE);
 	}
 
-	/*
-	 * Set_memory_decrypted() will change the memory contents if
-	 * decryption occurs, so zero monitor pages here.
-	 */
-	memset(vmbus_connection.monitor_pages[0], 0x00, HV_HYP_PAGE_SIZE);
-	memset(vmbus_connection.monitor_pages[1], 0x00, HV_HYP_PAGE_SIZE);
-
 	msginfo = kzalloc(sizeof(*msginfo) +
 			  sizeof(struct vmbus_channel_initiate_contact),
 			  GFP_KERNEL);
diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c
index fe50090dcc011..6e836a02910a0 100644
--- a/drivers/hv/hv.c
+++ b/drivers/hv/hv.c
@@ -123,12 +123,13 @@ static int hv_alloc_page(void **page, bool decrypt, const char *note)
 	if (!*page)
 		return -ENOMEM;
 
-	if (decrypt)
+	if (decrypt) {
 		ret = set_memory_decrypted((unsigned long)*page, 1);
-	if (ret)
-		goto failed;
-
-	memset(*page, 0, PAGE_SIZE);
+		if (ret)
+			goto failed;
+	} else {
+		memset(*page, 0, PAGE_SIZE);
+	}
 	return 0;
 
 failed:
diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c
index 31256cb22b39e..af6b1531d6006 100644
--- a/drivers/hv/hv_common.c
+++ b/drivers/hv/hv_common.c
@@ -505,8 +505,6 @@ int hv_common_cpu_init(unsigned int cpu)
 				/* It may be unsafe to free 'mem' */
 				return ret;
 			}
-
-			memset(mem, 0x00, pgcount * HV_HYP_PAGE_SIZE);
 		}
 
 		/*
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 58c624513cd43..59baf2687612b 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -1627,7 +1627,7 @@ static void *iommu_dma_alloc_pages(struct device *dev, size_t size,
 	struct page *page = NULL;
 	void *cpu_addr;
 
-	page = dma_alloc_contiguous(dev, alloc_size, gfp);
+	page = dma_alloc_contiguous(dev, alloc_size, gfp, 0);
 	if (!page)
 		page = alloc_pages_node(node, gfp, get_order(alloc_size));
 	if (!page)
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index e9807af235373..a8f37fb978452 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -8,6 +8,7 @@
 #include <linux/acpi_iort.h>
 #include <linux/bitfield.h>
 #include <linux/bitmap.h>
+#include <linux/cc_shared.h>
 #include <linux/cpu.h>
 #include <linux/crash_dump.h>
 #include <linux/delay.h>
@@ -19,7 +20,6 @@
 #include <linux/irqdomain.h>
 #include <linux/list.h>
 #include <linux/log2.h>
-#include <linux/mem_encrypt.h>
 #include <linux/memblock.h>
 #include <linux/mm.h>
 #include <linux/msi.h>
@@ -29,9 +29,9 @@
 #include <linux/of_pci.h>
 #include <linux/of_platform.h>
 #include <linux/percpu.h>
-#include <linux/set_memory.h>
 #include <linux/slab.h>
 #include <linux/syscore_ops.h>
+#include <linux/topology.h>
 
 #include <linux/irqchip.h>
 #include <linux/irqchip/arm-gic-v3.h>
@@ -195,6 +195,7 @@ static DEFINE_PER_CPU(struct cpu_lpi_count, cpu_lpi_count);
 static LIST_HEAD(its_nodes);
 static DEFINE_RAW_SPINLOCK(its_lock);
 static struct rdists *gic_rdists;
+static unsigned int vpe_l1_prealloc_order = UINT_MAX;
 static struct irq_domain *its_parent;
 
 static unsigned long its_list_map;
@@ -213,41 +214,31 @@ static gfp_t gfp_flags_quirk;
 static struct page *its_alloc_pages_node(int node, gfp_t gfp,
 					 unsigned int order)
 {
-	struct page *page;
-	int ret = 0;
-
-	page = alloc_pages_node(node, gfp | gfp_flags_quirk, order);
-
-	if (!page)
-		return NULL;
+	struct cc_shared_pages mem;
 
-	ret = set_memory_decrypted((unsigned long)page_address(page),
-				   1 << order);
-	/*
-	 * If set_memory_decrypted() fails then we don't know what state the
-	 * page is in, so we can't free it. Instead we leak it.
-	 * set_memory_decrypted() will already have WARNed.
-	 */
-	if (ret)
+	if (alloc_cc_shared_pages_node(node, gfp | gfp_flags_quirk,
+				       PAGE_SIZE << order, &mem))
 		return NULL;
 
-	return page;
+	return mem.page;
 }
 
 static struct page *its_alloc_pages(gfp_t gfp, unsigned int order)
 {
-	return its_alloc_pages_node(NUMA_NO_NODE, gfp, order);
+	return its_alloc_pages_node(numa_mem_id(), gfp, order);
 }
 
 static void its_free_pages(void *addr, unsigned int order)
 {
-	/*
-	 * If the memory cannot be encrypted again then we must leak the pages.
-	 * set_memory_encrypted() will already have WARNed.
-	 */
-	if (set_memory_encrypted((unsigned long)addr, 1 << order))
+	struct cc_shared_layout layout;
+	struct cc_shared_pages mem;
+
+	if (WARN_ON(cc_shared_calc_layout(PAGE_SIZE << order, &layout)))
 		return;
-	free_pages((unsigned long)addr, order);
+
+	mem.page = virt_to_page(addr);
+	mem.shared_size = layout.shared_size;
+	free_cc_shared_pages(&mem);
 }
 
 static struct gen_pool *itt_pool;
@@ -272,7 +263,8 @@ static void *itt_alloc_pool(int node, int size)
 		if (!page)
 			break;
 
-		gen_pool_add(itt_pool, (unsigned long)page_address(page), PAGE_SIZE, node);
+		gen_pool_add(itt_pool, (unsigned long)page_address(page),
+			     cc_shared_granule_size(), node);
 	} while (!addr);
 
 	return (void *)addr;
@@ -2893,12 +2885,35 @@ static bool allocate_vpe_l2_table(int cpu, u32 id)
 	return true;
 }
 
-static int allocate_vpe_l1_table(void)
+static unsigned int vpe_l1_table_order(u64 val)
+{
+	unsigned int psz;
+	u64 npg;
+
+	switch (FIELD_GET(GICR_VPROPBASER_4_1_PAGE_SIZE, val)) {
+	default:
+	case GIC_PAGE_SIZE_4K:
+		psz = SZ_4K;
+		break;
+	case GIC_PAGE_SIZE_16K:
+		psz = SZ_16K;
+		break;
+	case GIC_PAGE_SIZE_64K:
+		psz = SZ_64K;
+		break;
+	}
+
+	npg = FIELD_GET(GICR_VPROPBASER_4_1_SIZE, val) + 1;
+	return get_order(npg * psz);
+}
+
+static int allocate_vpe_l1_table(bool use_prealloc)
 {
 	void __iomem *vlpi_base = gic_data_rdist_vlpi_base();
 	u64 val, gpsz, npg, pa;
 	unsigned int psz = SZ_64K;
 	unsigned int np, epp, esz;
+	unsigned int order;
 	struct page *page;
 
 	if (!gic_rdists->has_rvpeid)
@@ -2991,7 +3006,16 @@ static int allocate_vpe_l1_table(void)
 
 	pr_debug("np = %d, npg = %lld, psz = %d, epp = %d, esz = %d\n",
 		 np, npg, psz, epp, esz);
-	page = its_alloc_pages(GFP_ATOMIC | __GFP_ZERO, get_order(np * PAGE_SIZE));
+	order = get_order(np * PAGE_SIZE);
+	if (use_prealloc) {
+		if (WARN_ON_ONCE(order != vpe_l1_prealloc_order))
+			return -EINVAL;
+
+		page = gic_data_rdist()->vpe_l1_prealloc;
+		gic_data_rdist()->vpe_l1_prealloc = NULL;
+	} else {
+		page = its_alloc_pages(GFP_KERNEL | __GFP_ZERO, order);
+	}
 	if (!page)
 		return -ENOMEM;
 
@@ -3008,6 +3032,9 @@ static int allocate_vpe_l1_table(void)
 	val |= GICR_VPROPBASER_4_1_VALID;
 
 out:
+	if (!use_prealloc)
+		vpe_l1_prealloc_order = vpe_l1_table_order(val);
+
 	gicr_write_vpropbaser(val, vlpi_base + GICR_VPROPBASER);
 	cpumask_set_cpu(smp_processor_id(), gic_data_rdist()->vpe_table_mask);
 
@@ -3147,7 +3174,7 @@ static u64 its_clear_vpend_valid(void __iomem *vlpi_base, u64 clr, u64 set)
 	return val;
 }
 
-static void its_cpu_init_lpis(void)
+static void its_cpu_init_lpis(bool use_prealloc)
 {
 	void __iomem *rbase = gic_data_rdist_rd_base();
 	struct page *pend_page;
@@ -3260,7 +3287,7 @@ static void its_cpu_init_lpis(void)
 		val = its_clear_vpend_valid(vlpi_base, 0, 0);
 	}
 
-	if (allocate_vpe_l1_table()) {
+	if (allocate_vpe_l1_table(use_prealloc)) {
 		/*
 		 * If the allocation has failed, we're in massive trouble.
 		 * Disable direct injection, and pray that no VM was
@@ -5431,7 +5458,7 @@ static int redist_disable_lpis(void)
 	return 0;
 }
 
-int its_cpu_init(void)
+int its_cpu_init(bool use_prealloc)
 {
 	if (!list_empty(&its_nodes)) {
 		int ret;
@@ -5440,13 +5467,66 @@ int its_cpu_init(void)
 		if (ret)
 			return ret;
 
-		its_cpu_init_lpis();
+		its_cpu_init_lpis(use_prealloc);
 		its_cpu_init_collections();
 	}
 
 	return 0;
 }
 
+static int its_vpe_l1_prepare(unsigned int cpu)
+{
+	struct page **prealloc;
+
+	if (!gic_rdists->has_rvpeid ||
+	    vpe_l1_prealloc_order == UINT_MAX ||
+	    (gic_data_rdist_cpu(cpu)->flags & RD_LOCAL_LPI_ENABLED))
+		return 0;
+
+	prealloc = &gic_data_rdist_cpu(cpu)->vpe_l1_prealloc;
+	if (*prealloc)
+		return 0;
+
+	*prealloc = its_alloc_pages_node(cpu_to_node(cpu),
+					 GFP_KERNEL | __GFP_ZERO,
+					 vpe_l1_prealloc_order);
+	return *prealloc ? 0 : -ENOMEM;
+}
+
+static int its_vpe_l1_cleanup(unsigned int cpu)
+{
+	struct page *page;
+
+	page = xchg(&gic_data_rdist_cpu(cpu)->vpe_l1_prealloc, NULL);
+	if (page)
+		its_free_pages(page_address(page), vpe_l1_prealloc_order);
+
+	return 0;
+}
+
+static int __init its_vpe_l1_cpuhp_init(void)
+{
+	int prepare_state, state;
+
+	state = cpuhp_setup_state_nocalls(CPUHP_BP_PREPARE_DYN,
+					  "irqchip/arm/gicv3-vpe:prepare",
+					  its_vpe_l1_prepare,
+					  its_vpe_l1_cleanup);
+	if (state < 0)
+		return state;
+	prepare_state = state;
+
+	state = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
+					  "irqchip/arm/gicv3-vpe:online",
+					  its_vpe_l1_cleanup, NULL);
+	if (state < 0) {
+		cpuhp_remove_state_nocalls(prepare_state);
+		return state;
+	}
+
+	return 0;
+}
+
 static void rdist_memreserve_cpuhp_cleanup_workfn(struct work_struct *work)
 {
 	cpuhp_remove_state_nocalls(gic_rdists->cpuhp_memreserve_state);
@@ -5871,6 +5951,15 @@ int __init its_init(struct fwnode_handle *handle, struct rdists *rdists,
 	if (WARN_ON(!has_v4_1 && rdists->has_rvpeid))
 		rdists->has_rvpeid = false;
 
+	if (rdists->has_rvpeid) {
+		err = its_vpe_l1_cpuhp_init();
+		if (err) {
+			rdists->has_rvpeid = false;
+			rdists->has_vlpis = false;
+			pr_err("ITS: Failed to prepare VPE tables, disabling GICv4 support\n");
+		}
+	}
+
 	if (has_v4 & rdists->has_vlpis) {
 		const struct irq_domain_ops *sgi_ops;
 
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 6e1fa5b247fc4..e2e00372dcfb0 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -1320,7 +1320,7 @@ static int gic_starting_cpu(unsigned int cpu)
 	gic_cpu_init();
 
 	if (gic_dist_supports_lpis())
-		its_cpu_init();
+		its_cpu_init(true);
 
 	return 0;
 }
@@ -2053,7 +2053,7 @@ static int __init gic_init_bases(phys_addr_t dist_phys_base,
 
 	if (gic_dist_supports_lpis()) {
 		its_init(handle, &gic_data.rdists, gic_data.domain, dist_prio_irq);
-		its_cpu_init();
+		its_cpu_init(false);
 		its_lpi_memreserve_init();
 	} else {
 		if (IS_ENABLED(CONFIG_ARM_GIC_V2M))
diff --git a/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c b/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c
index 26fe9c3f22e36..7d922e0a28f96 100644
--- a/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c
+++ b/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c
@@ -13,6 +13,7 @@
 #include <linux/mem_encrypt.h>
 #include <linux/mm.h>
 #include <linux/pgtable.h>
+#include <linux/string.h>
 
 #include <asm/hypervisor.h>
 
@@ -61,6 +62,8 @@ static int pkvm_set_memory_encrypted(unsigned long addr, int numpages)
 
 static int pkvm_set_memory_decrypted(unsigned long addr, int numpages)
 {
+	memset((void *)addr, 0, (size_t)numpages << PAGE_SHIFT);
+
 	return __set_memory_range(ARM_SMCCC_VENDOR_HYP_KVM_MEM_SHARE_FUNC_ID,
 				  addr, numpages);
 }
diff --git a/include/linux/cc_shared.h b/include/linux/cc_shared.h
new file mode 100644
index 0000000000000..5f8db7c468c5c
--- /dev/null
+++ b/include/linux/cc_shared.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_CC_SHARED_H
+#define _LINUX_CC_SHARED_H
+
+#include <linux/gfp_types.h>
+#include <linux/types.h>
+
+struct page;
+
+struct cc_shared_pages {
+	struct page *page;
+	size_t shared_size;
+};
+
+struct cc_shared_layout {
+	size_t requested_size;
+	size_t shared_size;
+	size_t alignment;
+};
+
+/*
+ * Architectures may override this to return the granule used for transitions
+ * between private and shared memory. The value must be a power of two and no
+ * smaller than PAGE_SIZE.
+ */
+size_t arch_cc_shared_granule_size(void);
+
+size_t cc_shared_granule_size(void);
+int cc_shared_calc_layout(size_t requested, struct cc_shared_layout *layout);
+bool cc_shared_range_valid(phys_addr_t base, size_t size);
+int cc_make_shared(void *addr, size_t size);
+int cc_make_private(void *addr, size_t size);
+int alloc_cc_shared_pages_node(int nid, gfp_t gfp,
+		size_t requested, struct cc_shared_pages *mem);
+int alloc_cc_shared_pages(gfp_t gfp,
+		size_t requested, struct cc_shared_pages *mem);
+void free_cc_shared_pages(struct cc_shared_pages *mem);
+
+#endif /* _LINUX_CC_SHARED_H */
diff --git a/include/linux/dma-map-ops.h b/include/linux/dma-map-ops.h
index 8fae2b7deb20d..f3490dd3f8ace 100644
--- a/include/linux/dma-map-ops.h
+++ b/include/linux/dma-map-ops.h
@@ -99,10 +99,11 @@ int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base,
 		phys_addr_t limit, struct cma **res_cma, bool fixed);
 
 struct page *dma_alloc_from_contiguous(struct device *dev, size_t count,
-				       unsigned int order, bool no_warn);
+		unsigned int order, unsigned long attrs, bool no_warn);
 bool dma_release_from_contiguous(struct device *dev, struct page *pages,
 				 int count);
-struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp);
+struct page *dma_alloc_contiguous(struct device *dev, size_t size,
+				  gfp_t gfp, unsigned long attrs);
 void dma_free_contiguous(struct device *dev, struct page *page, size_t size);
 
 void dma_contiguous_early_fixup(phys_addr_t base, unsigned long size);
@@ -125,7 +126,7 @@ static inline int dma_contiguous_reserve_area(phys_addr_t size,
 	return -ENOSYS;
 }
 static inline struct page *dma_alloc_from_contiguous(struct device *dev,
-		size_t count, unsigned int order, bool no_warn)
+		size_t count, unsigned int order, unsigned long attrs, bool no_warn)
 {
 	return NULL;
 }
@@ -136,7 +137,7 @@ static inline bool dma_release_from_contiguous(struct device *dev,
 }
 /* Use fallback alloc() and free() when CONFIG_DMA_CMA=n */
 static inline struct page *dma_alloc_contiguous(struct device *dev, size_t size,
-		gfp_t gfp)
+		gfp_t gfp, unsigned long attrs)
 {
 	return NULL;
 }
diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
index ea5fd2374ebe0..a5565a7451009 100644
--- a/include/linux/irqchip/arm-gic-v3.h
+++ b/include/linux/irqchip/arm-gic-v3.h
@@ -621,6 +621,7 @@ struct rdists {
 		u64             flags;
 		cpumask_t	*vpe_table_mask;
 		void		*vpe_l1_base;
+		struct page	*vpe_l1_prealloc;
 	} __percpu		*rdist;
 	phys_addr_t		prop_table_pa;
 	void			*prop_table_va;
@@ -637,7 +638,7 @@ struct rdists {
 struct irq_domain;
 struct fwnode_handle;
 int __init its_lpi_memreserve_init(void);
-int its_cpu_init(void);
+int its_cpu_init(bool use_prealloc);
 int its_init(struct fwnode_handle *handle, struct rdists *rdists,
 	     struct irq_domain *domain, u8 irq_prio);
 int mbi_init(struct fwnode_handle *fwnode, struct irq_domain *parent);
diff --git a/kernel/dma/contiguous.c b/kernel/dma/contiguous.c
index 66093460584e8..1a29565c91858 100644
--- a/kernel/dma/contiguous.c
+++ b/kernel/dma/contiguous.c
@@ -39,6 +39,7 @@
 
 #include <asm/page.h>
 
+#include <linux/cc_shared.h>
 #include <linux/memblock.h>
 #include <linux/err.h>
 #include <linux/sizes.h>
@@ -357,19 +358,29 @@ int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base,
  * dma_alloc_from_contiguous() - allocate pages from contiguous area
  * @dev:   Pointer to device for which the allocation is performed.
  * @count: Requested number of pages.
- * @align: Requested alignment of pages (in PAGE_SIZE order).
+ * @align: Preferred alignment of pages (in PAGE_SIZE order).
+ * @attrs: DMA allocation attributes.
  * @no_warn: Avoid printing message about failed allocation.
  *
  * This function allocates memory buffer for specified device. It uses
  * device specific contiguous memory area if available or the default
  * global one. Requires architecture specific dev_get_cma_area() helper
  * function.
+ *
+ * The preferred alignment is capped at CONFIG_CMA_ALIGNMENT. The internal
+ * shared-allocation attribute requires at least the architecture shared
+ * granule alignment and fails if that exceeds the CMA alignment limit.
  */
 struct page *dma_alloc_from_contiguous(struct device *dev, size_t count,
-				       unsigned int align, bool no_warn)
+		unsigned int align, unsigned long attrs, bool no_warn)
 {
-	if (align > CONFIG_CMA_ALIGNMENT)
-		align = CONFIG_CMA_ALIGNMENT;
+	unsigned int required_align = 0;
+
+	if (attrs & __DMA_ATTR_ALLOC_CC_SHARED)
+		required_align = get_order(cc_shared_granule_size());
+	if (required_align > CONFIG_CMA_ALIGNMENT)
+		return NULL;
+	align = min(max(align, required_align), CONFIG_CMA_ALIGNMENT);
 
 	return cma_alloc(dev_get_cma_area(dev), count, align, no_warn);
 }
@@ -390,10 +401,9 @@ bool dma_release_from_contiguous(struct device *dev, struct page *pages,
 	return cma_release(dev_get_cma_area(dev), pages, count);
 }
 
-static struct page *cma_alloc_aligned(struct cma *cma, size_t size, gfp_t gfp)
+static struct page *cma_alloc_aligned(struct cma *cma, size_t size, gfp_t gfp,
+				      unsigned int align)
 {
-	unsigned int align = min(get_order(size), CONFIG_CMA_ALIGNMENT);
-
 	return cma_alloc(cma, size >> PAGE_SHIFT, align, gfp & __GFP_NOWARN);
 }
 
@@ -402,6 +412,7 @@ static struct page *cma_alloc_aligned(struct cma *cma, size_t size, gfp_t gfp)
  * @dev:   Pointer to device for which the allocation is performed.
  * @size:  Requested allocation size.
  * @gfp:   Allocation flags.
+ * @attrs: DMA allocation attributes.
  *
  * tries to use device specific contiguous memory area if available, or it
  * tries to use per-numa cma, if the allocation fails, it will fallback to
@@ -412,8 +423,11 @@ static struct page *cma_alloc_aligned(struct cma *cma, size_t size, gfp_t gfp)
  * there is no need to waste CMA pages for that kind; it also helps reduce
  * fragmentations.
  */
-struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp)
+struct page *dma_alloc_contiguous(struct device *dev, size_t size,
+				  gfp_t gfp, unsigned long attrs)
 {
+	unsigned int required_align = 0;
+	unsigned int align = get_order(size);
 #ifdef CONFIG_DMA_NUMA_CMA
 	int nid = dev_to_node(dev);
 #endif
@@ -421,8 +435,13 @@ struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp)
 	/* CMA can be used only in the context which permits sleeping */
 	if (!gfpflags_allow_blocking(gfp))
 		return NULL;
+	if (attrs & __DMA_ATTR_ALLOC_CC_SHARED)
+		required_align = get_order(cc_shared_granule_size());
+	if (required_align > CONFIG_CMA_ALIGNMENT)
+		return NULL;
+	align = min(max(align, required_align), CONFIG_CMA_ALIGNMENT);
 	if (dev->cma_area)
-		return cma_alloc_aligned(dev->cma_area, size, gfp);
+		return cma_alloc_aligned(dev->cma_area, size, gfp, align);
 	if (size <= PAGE_SIZE)
 		return NULL;
 
@@ -431,7 +450,7 @@ struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp)
 		struct cma *cma = dma_contiguous_numa_area[nid];
 		struct page *page;
 		if (cma) {
-			page = cma_alloc_aligned(cma, size, gfp);
+			page = cma_alloc_aligned(cma, size, gfp, align);
 			if (page)
 				return page;
 		}
@@ -440,7 +459,7 @@ struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp)
 	if (!dma_contiguous_default_area)
 		return NULL;
 
-	return cma_alloc_aligned(dma_contiguous_default_area, size, gfp);
+	return cma_alloc_aligned(dma_contiguous_default_area, size, gfp, align);
 }
 
 /**
diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
index da665ca22d5c0..09a8a842fc6fc 100644
--- a/kernel/dma/direct.c
+++ b/kernel/dma/direct.c
@@ -11,10 +11,10 @@
 #include <linux/scatterlist.h>
 #include <linux/pfn.h>
 #include <linux/vmalloc.h>
-#include <linux/set_memory.h>
 #include <linux/slab.h>
 #include <linux/pci-p2pdma.h>
 #include <linux/cc_platform.h>
+#include <linux/cc_shared.h>
 
 #include "direct.h"
 
@@ -85,7 +85,7 @@ static int dma_set_decrypted(struct device *dev, void *vaddr, size_t size)
 {
 	int ret;
 
-	ret = set_memory_decrypted((unsigned long)vaddr, PFN_UP(size));
+	ret = cc_make_shared(vaddr, size);
 	if (ret)
 		pr_warn_ratelimited("leaking DMA memory that can't be decrypted\n");
 	return ret;
@@ -95,7 +95,7 @@ static int dma_set_encrypted(struct device *dev, void *vaddr, size_t size)
 {
 	int ret;
 
-	ret = set_memory_encrypted((unsigned long)vaddr, PFN_UP(size));
+	ret = cc_make_private(vaddr, size);
 	if (ret)
 		pr_warn_ratelimited("leaking DMA memory that can't be re-encrypted\n");
 	return ret;
@@ -115,7 +115,7 @@ static struct page *dma_direct_alloc_swiotlb(struct device *dev, size_t size,
 }
 
 static struct page *__dma_direct_alloc_pages(struct device *dev, size_t size,
-		gfp_t gfp, bool allow_highmem)
+		gfp_t gfp, bool allow_highmem, unsigned long attrs)
 {
 	int node = dev_to_node(dev);
 	struct page *page;
@@ -124,7 +124,7 @@ static struct page *__dma_direct_alloc_pages(struct device *dev, size_t size,
 	WARN_ON_ONCE(!PAGE_ALIGNED(size));
 
 	gfp |= dma_direct_optimal_gfp_mask(dev, &phys_limit);
-	page = dma_alloc_contiguous(dev, size, gfp);
+	page = dma_alloc_contiguous(dev, size, gfp, attrs);
 	if (page) {
 		if (dma_coherent_ok(dev, page_to_phys(page), size) &&
 		    (allow_highmem || !PageHighMem(page)))
@@ -184,7 +184,7 @@ static void *dma_direct_alloc_no_mapping(struct device *dev, size_t size,
 {
 	struct page *page;
 
-	page = __dma_direct_alloc_pages(dev, size, gfp & ~__GFP_ZERO, true);
+	page = __dma_direct_alloc_pages(dev, size, gfp & ~__GFP_ZERO, true, 0);
 	if (!page)
 		return NULL;
 
@@ -205,13 +205,15 @@ void *dma_direct_alloc(struct device *dev, size_t size,
 	bool remap = false, set_uncached = false;
 	bool mark_mem_decrypt = false;
 	bool allow_highmem = true;
+	struct cc_shared_layout layout;
 	struct page *page;
 	void *cpu_addr;
 
 	if (force_dma_unencrypted(dev))
 		attrs |= __DMA_ATTR_ALLOC_CC_SHARED;
 
-	if (attrs & __DMA_ATTR_ALLOC_CC_SHARED) {
+	mark_mem_decrypt = attrs & __DMA_ATTR_ALLOC_CC_SHARED;
+	if (mark_mem_decrypt) {
 		/*
 		 * Unencrypted/shared DMA requires a linear-mapped buffer
 		 * address to look up the PFN and set architecture-required PFN
@@ -219,7 +221,6 @@ void *dma_direct_alloc(struct device *dev, size_t size,
 		 * allocation.
 		 */
 		allow_highmem = false;
-		mark_mem_decrypt = true;
 	}
 
 	size = PAGE_ALIGN(size);
@@ -285,8 +286,15 @@ void *dma_direct_alloc(struct device *dev, size_t size,
 		return NULL;
 	}
 
+	if (mark_mem_decrypt) {
+		if (cc_shared_calc_layout(size, &layout))
+			return NULL;
+		size = layout.shared_size;
+	}
+
 	/* we always manually zero the memory once we are done */
-	page = __dma_direct_alloc_pages(dev, size, gfp & ~__GFP_ZERO, allow_highmem);
+	page = __dma_direct_alloc_pages(dev, size, gfp & ~__GFP_ZERO,
+					allow_highmem, attrs);
 	if (!page)
 		return NULL;
 
@@ -305,7 +313,7 @@ void *dma_direct_alloc(struct device *dev, size_t size,
 		void *lm_addr;
 
 		lm_addr = page_address(page);
-		if (set_memory_decrypted((unsigned long)lm_addr, PFN_UP(size)))
+		if (dma_set_decrypted(dev, lm_addr, size))
 			goto out_leak_pages;
 	}
 
@@ -324,7 +332,9 @@ void *dma_direct_alloc(struct device *dev, size_t size,
 		cpu_addr = page_address(page);
 	}
 
-	memset(cpu_addr, 0, size);
+	/* Zero after remapping because the page may be in HighMem. */
+	if (!mark_mem_decrypt)
+		memset(cpu_addr, 0, size);
 
 	if (set_uncached) {
 		void *uncached_cpu_addr;
@@ -362,6 +372,7 @@ void dma_direct_free(struct device *dev, size_t size,
 	phys_addr_t phys;
 	bool mark_mem_encrypted = false;
 	struct io_tlb_pool *swiotlb_pool;
+	struct cc_shared_layout layout;
 	unsigned int page_order = get_order(size);
 
 	/*
@@ -406,6 +417,12 @@ void dma_direct_free(struct device *dev, size_t size,
 		/* Swiotlb doesn't need a page attribute update on free */
 		mark_mem_encrypted = false;
 
+	if (mark_mem_encrypted) {
+		if (WARN_ON_ONCE(cc_shared_calc_layout(size, &layout)))
+			return;
+		size = layout.shared_size;
+	}
+
 	if (is_vmalloc_addr(cpu_addr)) {
 		vunmap(cpu_addr);
 	} else {
@@ -417,10 +434,8 @@ void dma_direct_free(struct device *dev, size_t size,
 		void *lm_addr;
 
 		lm_addr = phys_to_virt(phys);
-		if (set_memory_encrypted((unsigned long)lm_addr, PFN_UP(size))) {
-			pr_warn_ratelimited("leaking DMA memory that can't be re-encrypted\n");
+		if (dma_set_encrypted(dev, lm_addr, size))
 			return;
-		}
 	}
 
 	if (swiotlb_pool)
@@ -433,12 +448,16 @@ struct page *dma_direct_alloc_pages(struct device *dev, size_t size,
 		dma_addr_t *dma_handle, enum dma_data_direction dir, gfp_t gfp)
 {
 	unsigned long attrs = 0;
+	struct cc_shared_layout layout;
 	struct page *page;
 	void *cpu_addr;
+	bool mark_mem_decrypt;
 
 	if (force_dma_unencrypted(dev))
 		attrs |= __DMA_ATTR_ALLOC_CC_SHARED;
 
+	mark_mem_decrypt = attrs & __DMA_ATTR_ALLOC_CC_SHARED;
+
 	if ((attrs & __DMA_ATTR_ALLOC_CC_SHARED) && dma_direct_use_pool(dev, gfp))
 		return dma_direct_alloc_from_pool(dev, size, dma_handle,
 						  &cpu_addr, gfp, attrs);
@@ -449,19 +468,28 @@ struct page *dma_direct_alloc_pages(struct device *dev, size_t size,
 			return NULL;
 
 		cpu_addr = page_address(page);
+		mark_mem_decrypt = false;
 		goto setup_page;
 	}
 
-	page = __dma_direct_alloc_pages(dev, size, gfp, false);
+	if (mark_mem_decrypt) {
+		if (cc_shared_calc_layout(size, &layout))
+			return NULL;
+		size = layout.shared_size;
+	}
+
+	page = __dma_direct_alloc_pages(dev, size, gfp, false, attrs);
 	if (!page)
 		return NULL;
 
 	cpu_addr = page_address(page);
-	if ((attrs & __DMA_ATTR_ALLOC_CC_SHARED) &&
-	    dma_set_decrypted(dev, cpu_addr, size))
-		goto out_leak_pages;
 setup_page:
-	memset(cpu_addr, 0, size);
+	if (mark_mem_decrypt) {
+		if (dma_set_decrypted(dev, cpu_addr, size))
+			goto out_leak_pages;
+	} else {
+		memset(cpu_addr, 0, size);
+	}
 	*dma_handle = phys_to_dma_direct(dev, page_to_phys(page),
 					 attrs & __DMA_ATTR_ALLOC_CC_SHARED);
 	return page;
@@ -476,6 +504,7 @@ void dma_direct_free_pages(struct device *dev, size_t size,
 	phys_addr_t phys;
 	void *vaddr = page_address(page);
 	struct io_tlb_pool *swiotlb_pool;
+	struct cc_shared_layout layout;
 	/*
 	 * if the device had requested for an unencrypted buffer,
 	 * convert it to encrypted on free
@@ -492,6 +521,12 @@ void dma_direct_free_pages(struct device *dev, size_t size,
 	if (swiotlb_pool)
 		mark_mem_encrypted = false;
 
+	if (mark_mem_encrypted) {
+		if (WARN_ON_ONCE(cc_shared_calc_layout(size, &layout)))
+			return;
+		size = layout.shared_size;
+	}
+
 	if (mark_mem_encrypted && dma_set_encrypted(dev, vaddr, size))
 		return;
 
diff --git a/kernel/dma/ops_helpers.c b/kernel/dma/ops_helpers.c
index 6b5f9208d31c0..8320cc0fada50 100644
--- a/kernel/dma/ops_helpers.c
+++ b/kernel/dma/ops_helpers.c
@@ -66,7 +66,7 @@ struct page *dma_common_alloc_pages(struct device *dev, size_t size,
 	struct page *page;
 	phys_addr_t phys;
 
-	page = dma_alloc_contiguous(dev, size, gfp);
+	page = dma_alloc_contiguous(dev, size, gfp, 0);
 	if (!page)
 		page = alloc_pages_node(dev_to_node(dev), gfp, get_order(size));
 	if (!page)
diff --git a/kernel/dma/pool.c b/kernel/dma/pool.c
index 00f422a1e8966..f059312c15aa5 100644
--- a/kernel/dma/pool.c
+++ b/kernel/dma/pool.c
@@ -4,12 +4,12 @@
  * Copyright (C) 2020 Google LLC
  */
 #include <linux/cma.h>
+#include <linux/cc_shared.h>
 #include <linux/debugfs.h>
 #include <linux/dma-map-ops.h>
 #include <linux/dma-direct.h>
 #include <linux/init.h>
 #include <linux/genalloc.h>
-#include <linux/set_memory.h>
 #include <linux/slab.h>
 #include <linux/workqueue.h>
 #include <linux/cc_platform.h>
@@ -85,6 +85,9 @@ static bool cma_in_zone(gfp_t gfp)
 static int atomic_pool_expand(struct dma_gen_pool *dma_pool, size_t pool_size,
 			      gfp_t gfp)
 {
+	struct cc_shared_layout layout;
+	unsigned long attrs = 0;
+	unsigned int min_order = 0;
 	unsigned int order;
 	struct page *page = NULL;
 	bool leak_pages = false;
@@ -92,6 +95,17 @@ static int atomic_pool_expand(struct dma_gen_pool *dma_pool, size_t pool_size,
 	int ret = -ENOMEM;
 	pgprot_t prot __maybe_unused;
 
+	if (dma_pool->cc_shared) {
+		ret = cc_shared_calc_layout(pool_size, &layout);
+		if (ret)
+			goto out;
+		pool_size = layout.shared_size;
+		min_order = get_order(layout.alignment);
+		if (min_order > MAX_PAGE_ORDER)
+			return -E2BIG;
+		attrs = __DMA_ATTR_ALLOC_CC_SHARED;
+	}
+
 	/* Cannot allocate larger than MAX_PAGE_ORDER */
 	order = min(get_order(pool_size), MAX_PAGE_ORDER);
 
@@ -99,10 +113,10 @@ static int atomic_pool_expand(struct dma_gen_pool *dma_pool, size_t pool_size,
 		pool_size = 1 << (PAGE_SHIFT + order);
 		if (cma_in_zone(gfp))
 			page = dma_alloc_from_contiguous(NULL, 1 << order,
-							 order, false);
+							 order, attrs, false);
 		if (!page)
 			page = alloc_pages(gfp | __GFP_NOWARN, order);
-	} while (!page && order-- > 0);
+	} while (!page && order-- > min_order);
 	if (!page)
 		goto out;
 
@@ -126,8 +140,7 @@ static int atomic_pool_expand(struct dma_gen_pool *dma_pool, size_t pool_size,
 	 * shrink so no re-encryption occurs in dma_direct_free().
 	 */
 	if (dma_pool->cc_shared) {
-		ret = set_memory_decrypted((unsigned long)page_to_virt(page),
-					   1 << order);
+		ret = cc_make_shared(page_to_virt(page), pool_size);
 		if (ret) {
 			leak_pages = true;
 			goto remove_mapping;
@@ -144,7 +157,7 @@ static int atomic_pool_expand(struct dma_gen_pool *dma_pool, size_t pool_size,
 
 encrypt_mapping:
 	if (dma_pool->cc_shared &&
-	    set_memory_encrypted((unsigned long)page_to_virt(page), 1 << order))
+	    cc_make_private(page_to_virt(page), pool_size))
 		leak_pages = true;
 
 remove_mapping:
diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
index ded7016a46a71..8d71f2e0fd855 100644
--- a/kernel/dma/swiotlb.c
+++ b/kernel/dma/swiotlb.c
@@ -22,6 +22,7 @@
 
 #include <linux/cache.h>
 #include <linux/cc_platform.h>
+#include <linux/cc_shared.h>
 #include <linux/ctype.h>
 #include <linux/debugfs.h>
 #include <linux/dma-direct.h>
@@ -38,7 +39,6 @@
 #include <linux/pfn.h>
 #include <linux/rculist.h>
 #include <linux/scatterlist.h>
-#include <linux/set_memory.h>
 #include <linux/spinlock.h>
 #include <linux/string.h>
 #include <linux/swiotlb.h>
@@ -330,6 +330,14 @@ static inline unsigned long nr_slots(u64 val)
 	return DIV_ROUND_UP(val, IO_TLB_SIZE);
 }
 
+static unsigned long swiotlb_align_nslabs(unsigned long nslabs)
+{
+	unsigned long granule_nslabs;
+
+	granule_nslabs = cc_shared_granule_size() >> IO_TLB_SHIFT;
+	return ALIGN(nslabs, granule_nslabs);
+}
+
 static void swiotlb_mark_pool_used(struct io_tlb_pool *pool)
 {
 	unsigned long i;
@@ -369,13 +377,13 @@ void __init swiotlb_update_mem_attributes(void)
 
 	if (!mem->nslabs || mem->late_alloc)
 		return;
-	bytes = PAGE_ALIGN(mem->nslabs << IO_TLB_SHIFT);
+
+	bytes = ALIGN(mem->nslabs << IO_TLB_SHIFT, cc_shared_granule_size());
 
 	if (io_tlb_default_mem.cc_shared) {
 		int ret;
 
-		ret = set_memory_decrypted((unsigned long)mem->vaddr,
-					   bytes >> PAGE_SHIFT);
+		ret = cc_make_shared(mem->vaddr, bytes);
 		if (ret) {
 			pr_warn("Failed to decrypt default memory pool, disabling it\n");
 			swiotlb_mark_pool_used(mem);
@@ -433,10 +441,11 @@ static void add_mem_pool(struct io_tlb_mem *mem, struct io_tlb_pool *pool)
 }
 
 static void __init *swiotlb_memblock_alloc(unsigned long nslabs,
-		unsigned int flags,
+		unsigned long *alloc_nslabs, unsigned int flags,
 		int (*remap)(void *tlb, unsigned long nslabs))
 {
-	size_t bytes = PAGE_ALIGN(nslabs << IO_TLB_SHIFT);
+	unsigned long aligned_nslabs = swiotlb_align_nslabs(nslabs);
+	size_t bytes = aligned_nslabs << IO_TLB_SHIFT;
 	void *tlb;
 
 	/*
@@ -445,9 +454,9 @@ static void __init *swiotlb_memblock_alloc(unsigned long nslabs,
 	 * memory encryption.
 	 */
 	if (flags & SWIOTLB_ANY)
-		tlb = memblock_alloc(bytes, PAGE_SIZE);
+		tlb = memblock_alloc(bytes, cc_shared_granule_size());
 	else
-		tlb = memblock_alloc_low(bytes, PAGE_SIZE);
+		tlb = memblock_alloc_low(bytes, cc_shared_granule_size());
 
 	if (!tlb) {
 		pr_warn("%s: Failed to allocate %zu bytes tlb structure\n",
@@ -455,12 +464,13 @@ static void __init *swiotlb_memblock_alloc(unsigned long nslabs,
 		return NULL;
 	}
 
-	if (remap && remap(tlb, nslabs) < 0) {
-		memblock_free(tlb, PAGE_ALIGN(bytes));
+	if (remap && remap(tlb, aligned_nslabs) < 0) {
+		memblock_free(tlb, bytes);
 		pr_warn("%s: Failed to remap %zu bytes\n", __func__, bytes);
 		return NULL;
 	}
 
+	*alloc_nslabs = aligned_nslabs;
 	return tlb;
 }
 
@@ -473,6 +483,7 @@ void __init swiotlb_init_remap(bool addressing_limit, unsigned int flags,
 {
 	struct io_tlb_pool *mem = &io_tlb_default_mem.defpool;
 	unsigned long nslabs;
+	unsigned long alloc_nslabs;
 	unsigned int nareas;
 	size_t alloc_size;
 	void *tlb;
@@ -497,13 +508,14 @@ void __init swiotlb_init_remap(bool addressing_limit, unsigned int flags,
 		swiotlb_adjust_nareas(num_possible_cpus());
 
 	nslabs = default_nslabs;
-	nareas = limit_nareas(default_nareas, nslabs);
-	while ((tlb = swiotlb_memblock_alloc(nslabs, flags, remap)) == NULL) {
+	while ((tlb = swiotlb_memblock_alloc(nslabs, &alloc_nslabs, flags,
+					     remap)) == NULL) {
 		if (nslabs <= IO_TLB_MIN_SLABS)
 			return;
 		nslabs = ALIGN(nslabs >> 1, IO_TLB_SEGSIZE);
-		nareas = limit_nareas(nareas, nslabs);
 	}
+	nslabs = alloc_nslabs;
+	nareas = limit_nareas(default_nareas, nslabs);
 
 	if (default_nslabs != nslabs) {
 		pr_info("SWIOTLB bounce buffer size adjusted %lu -> %lu slabs",
@@ -578,7 +590,7 @@ int swiotlb_init_late(size_t size, gfp_t gfp_mask,
 		swiotlb_adjust_nareas(num_possible_cpus());
 
 retry:
-	order = get_order(nslabs << IO_TLB_SHIFT);
+	order = get_order(ALIGN(nslabs << IO_TLB_SHIFT, cc_shared_granule_size()));
 	nslabs = SLABS_PER_PAGE << order;
 
 	while ((SLABS_PER_PAGE << order) > IO_TLB_MIN_SLABS) {
@@ -587,6 +599,8 @@ int swiotlb_init_late(size_t size, gfp_t gfp_mask,
 		if (vstart)
 			break;
 		order--;
+		if (order < get_order(cc_shared_granule_size()))
+			break;
 		nslabs = SLABS_PER_PAGE << order;
 		retried = true;
 	}
@@ -626,8 +640,7 @@ int swiotlb_init_late(size_t size, gfp_t gfp_mask,
 		goto error_slots;
 
 	if (io_tlb_default_mem.cc_shared) {
-		rc = set_memory_decrypted((unsigned long)vstart,
-					  (nslabs << IO_TLB_SHIFT) >> PAGE_SHIFT);
+		rc = cc_make_shared(vstart, nslabs << IO_TLB_SHIFT);
 		if (rc) {
 			leak_pages = true;
 			goto error_decrypt;
@@ -667,11 +680,11 @@ void __init swiotlb_exit(void)
 
 	pr_info("tearing down default memory pool\n");
 	tbl_vaddr = (unsigned long)phys_to_virt(mem->start);
-	tbl_size = PAGE_ALIGN(mem->end - mem->start);
+	tbl_size = ALIGN(mem->end - mem->start, cc_shared_granule_size());
 	slots_size = PAGE_ALIGN(array_size(sizeof(*mem->slots), mem->nslabs));
 
 	if (io_tlb_default_mem.cc_shared) {
-		if (set_memory_encrypted(tbl_vaddr, tbl_size >> PAGE_SHIFT))
+		if (cc_make_private((void *)tbl_vaddr, tbl_size))
 			leak_pages = true;
 	}
 
@@ -711,12 +724,15 @@ void __init swiotlb_exit(void)
 static struct page *alloc_dma_pages(gfp_t gfp, size_t bytes,
 		u64 phys_limit, unsigned long attrs)
 {
-	unsigned int order = get_order(bytes);
 	bool cc_shared = attrs & __DMA_ATTR_ALLOC_CC_SHARED;
+	unsigned int order;
 	struct page *page;
 	phys_addr_t paddr;
 	void *vaddr;
 
+	if (cc_shared)
+		bytes = ALIGN(bytes, cc_shared_granule_size());
+	order = get_order(bytes);
 	page = alloc_pages(gfp, order);
 	if (!page)
 		return NULL;
@@ -728,13 +744,13 @@ static struct page *alloc_dma_pages(gfp_t gfp, size_t bytes,
 	}
 
 	vaddr = phys_to_virt(paddr);
-	if (cc_shared && set_memory_decrypted((unsigned long)vaddr, PFN_UP(bytes)))
+	if (cc_shared && cc_make_shared(vaddr, bytes))
 		goto error;
 	return page;
 
 error:
 	/* Intentional leak if pages cannot be encrypted again. */
-	if (cc_shared && !set_memory_encrypted((unsigned long)vaddr, PFN_UP(bytes)))
+	if (cc_shared && !cc_make_private(vaddr, bytes))
 		__free_pages(page, order);
 	return NULL;
 }
@@ -807,9 +823,11 @@ static void swiotlb_free_tlb(void *vaddr, size_t bytes, bool cc_shared)
 	    dma_free_from_pool(NULL, vaddr, bytes))
 		return;
 
+	if (cc_shared)
+		bytes = ALIGN(bytes, cc_shared_granule_size());
 	/* Intentional leak if pages cannot be encrypted again. */
 	if (!cc_shared ||
-	    !set_memory_encrypted((unsigned long)vaddr, PFN_UP(bytes)))
+	    !cc_make_private(vaddr, bytes))
 		__free_pages(virt_to_page(vaddr), get_order(bytes));
 }
 
@@ -863,6 +881,12 @@ static struct io_tlb_pool *swiotlb_alloc_pool(struct device *dev,
 		tlb_size = nslabs << IO_TLB_SHIFT;
 	}
 
+	/* Transient pools are tied to one mapping and cannot reuse padding. */
+	if (mem->cc_shared && !dev) {
+		nslabs = swiotlb_align_nslabs(nslabs);
+		tlb_size = nslabs << IO_TLB_SHIFT;
+	}
+
 	slot_order = get_order(array_size(sizeof(*pool->slots), nslabs));
 	pool->slots = (struct io_tlb_slot *)
 		__get_free_pages(gfp, slot_order);
@@ -2002,6 +2026,14 @@ static int rmem_swiotlb_device_init(struct reserved_mem *rmem,
 		return -EINVAL;
 	}
 
+	if (cc_platform_has(CC_ATTR_MEM_ENCRYPT) &&
+	    !cc_shared_range_valid(rmem->base, rmem->size)) {
+		dev_err(dev,
+			"Restricted DMA pool must be aligned to %#zx bytes for memory encryption\n",
+			cc_shared_granule_size());
+		return -EINVAL;
+	}
+
 	/*
 	 * Since multiple devices can share the same pool, the private data,
 	 * io_tlb_mem struct, will be initialized by the first device attached
@@ -2035,8 +2067,8 @@ static int rmem_swiotlb_device_init(struct reserved_mem *rmem,
 			int ret;
 
 			mem->cc_shared = true;
-			ret = set_memory_decrypted((unsigned long)phys_to_virt(rmem->base),
-						   rmem->size >> PAGE_SHIFT);
+			ret = cc_make_shared(phys_to_virt(rmem->base),
+					     rmem->size);
 			if (ret) {
 				dev_err(dev, "Failed to decrypt restricted DMA pool\n");
 				kfree(pool->areas);
diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
index 59fb9d71e9d86..2a337ce7264e4 100644
--- a/kernel/kexec_file.c
+++ b/kernel/kexec_file.c
@@ -682,7 +682,8 @@ static int kexec_alloc_contig(struct kexec_buf *kbuf)
 	if (kbuf->image->type == KEXEC_TYPE_CRASH)
 		return -EPERM;
 
-	p = dma_alloc_from_contiguous(NULL, nr_pages, get_order(kbuf->buf_align), true);
+	p = dma_alloc_from_contiguous(NULL, nr_pages,
+				      get_order(kbuf->buf_align), 0, true);
 	if (!p)
 		return -ENOMEM;
 
diff --git a/mm/Makefile b/mm/Makefile
index e7245cb88c665..6e65444284226 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -56,6 +56,7 @@ obj-y			:= filemap.o mempool.o oom_kill.o fadvise.o \
 			   compaction.o show_mem.o \
 			   interval_tree.o list_lru.o workingset.o \
 			   debug.o gup.o mmap_lock.o vma_init.o $(mmu-y)
+obj-y			+= cc_shared.o
 
 # Give 'page_alloc' its own module-parameter namespace
 page-alloc-y := page_alloc.o
diff --git a/mm/cc_shared.c b/mm/cc_shared.c
new file mode 100644
index 0000000000000..6f68a3f44ef80
--- /dev/null
+++ b/mm/cc_shared.c
@@ -0,0 +1,232 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 ARM Ltd.
+ */
+#include <linux/align.h>
+#include <linux/cc_platform.h>
+#include <linux/cc_shared.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/gfp.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/mm.h>
+#include <linux/mem_encrypt.h>
+#include <linux/numa.h>
+#include <linux/overflow.h>
+#include <linux/set_memory.h>
+
+size_t __weak arch_cc_shared_granule_size(void)
+{
+	return PAGE_SIZE;
+}
+
+size_t cc_shared_granule_size(void)
+{
+	size_t granule = arch_cc_shared_granule_size();
+
+	if (WARN_ON_ONCE(granule < PAGE_SIZE || !is_power_of_2(granule)))
+		return PAGE_SIZE;
+
+	return granule;
+}
+EXPORT_SYMBOL_GPL(cc_shared_granule_size);
+
+int cc_shared_calc_layout(size_t requested, struct cc_shared_layout *layout)
+{
+	size_t granule, rounded;
+
+	if (!requested || !layout)
+		return -EINVAL;
+
+	granule = cc_shared_granule_size();
+	if (check_add_overflow(requested, granule - 1, &rounded))
+		return -EOVERFLOW;
+
+	rounded = ALIGN_DOWN(rounded, granule);
+	layout->requested_size = requested;
+	layout->shared_size = rounded;
+	layout->alignment = granule;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cc_shared_calc_layout);
+
+bool cc_shared_range_valid(phys_addr_t base, size_t size)
+{
+	size_t granule = cc_shared_granule_size();
+
+	if (!size)
+		return false;
+
+	return IS_ALIGNED(base, granule) && IS_ALIGNED(size, granule);
+}
+EXPORT_SYMBOL_GPL(cc_shared_range_valid);
+
+static int cc_validate_transition(void *addr, size_t size)
+{
+	phys_addr_t phys;
+
+	if (!addr || !size || !PAGE_ALIGNED(addr) ||
+	    !virt_addr_valid(addr))
+		return -EINVAL;
+
+	phys = page_to_phys(virt_to_page(addr));
+	if (!cc_shared_range_valid(phys, size))
+		return -EINVAL;
+
+	return 0;
+}
+
+int cc_make_shared(void *addr, size_t size)
+{
+	int ret = cc_validate_transition(addr, size);
+
+	if (ret)
+		return ret;
+
+	return set_memory_decrypted((unsigned long)addr, size >> PAGE_SHIFT);
+}
+
+int cc_make_private(void *addr, size_t size)
+{
+	int ret = cc_validate_transition(addr, size);
+
+	if (ret)
+		return ret;
+
+	return set_memory_encrypted((unsigned long)addr, size >> PAGE_SHIFT);
+}
+
+static int __alloc_cc_shared_pages_node(int nid, gfp_t gfp,
+					size_t requested,
+					struct cc_shared_pages *mem)
+{
+	struct cc_shared_layout layout;
+	struct page *page;
+	unsigned int order;
+	int ret;
+
+	might_sleep();
+
+	ret = cc_shared_calc_layout(requested, &layout);
+	if (ret)
+		return ret;
+
+	order = get_order(layout.shared_size);
+	if (order > MAX_PAGE_ORDER)
+		return -EINVAL;
+
+	/*
+	 * State transitions require a linear-map address and may modify memory.
+	 * Allocate from low memory and let the architecture place zeroing at the
+	 * appropriate point in the transition.
+	 */
+	gfp &= ~(__GFP_HIGHMEM | __GFP_ZERO);
+	if (nid == NUMA_NO_NODE)
+		page = alloc_pages(gfp, order);
+	else
+		page = alloc_pages_node(nid, gfp, order);
+	if (!page)
+		return -ENOMEM;
+
+	ret = cc_make_shared(page_address(page), layout.shared_size);
+	if (ret) {
+		if (!cc_make_private(page_address(page), layout.shared_size))
+			__free_pages(page, order);
+		else
+			pr_warn_ratelimited("leaking %zu bytes with uncertain shared state\n",
+					    layout.shared_size);
+		return ret;
+	}
+
+	mem->page = page;
+	mem->shared_size = layout.shared_size;
+	return 0;
+}
+
+/**
+ * alloc_cc_shared_pages_node - allocate memory that can be shared
+ * @nid: NUMA node from which to allocate, or %NUMA_NO_NODE
+ * @gfp: allocation flags
+ * @requested: number of bytes requested; must be nonzero
+ * @mem: storage for the allocated page and the size of the shared range
+ *
+ * Allocate at least @requested bytes and make the allocation shared when
+ * memory encryption is active.  A memory-state transition requires a valid
+ * linear-map address, so such allocations never come from high memory
+ *
+ * The shared range may be rounded up to the architecture's transition
+ * granule.  On success, @mem->shared_size records the actual size that was
+ * made shared and must be retained unchanged for free_cc_shared_pages().
+ * @mem is not modified on failure.
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+int alloc_cc_shared_pages_node(int nid, gfp_t gfp,
+			       size_t requested,
+			       struct cc_shared_pages *mem)
+{
+	struct page *page;
+	unsigned int order;
+
+	if (!mem || !requested)
+		return -EINVAL;
+
+	if (cc_platform_has(CC_ATTR_MEM_ENCRYPT))
+		return __alloc_cc_shared_pages_node(nid, gfp, requested, mem);
+
+	order = get_order(requested);
+	if (order > MAX_PAGE_ORDER)
+		return -EINVAL;
+
+	if (nid == NUMA_NO_NODE)
+		page = alloc_pages(gfp, order);
+	else
+		page = alloc_pages_node(nid, gfp, order);
+	if (!page)
+		return -ENOMEM;
+
+	mem->page = page;
+	mem->shared_size = requested;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(alloc_cc_shared_pages_node);
+
+/**
+ * alloc_cc_shared_pages - allocate memory that can be shared
+ * @gfp: allocation flags
+ * @requested: number of bytes requested; must be nonzero
+ * @mem: storage for the allocated page and the size of the shared range
+ *
+ * Equivalent to alloc_cc_shared_pages_node() with %NUMA_NO_NODE.
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+int alloc_cc_shared_pages(gfp_t gfp,
+			  size_t requested, struct cc_shared_pages *mem)
+{
+	return alloc_cc_shared_pages_node(NUMA_NO_NODE, gfp, requested, mem);
+}
+EXPORT_SYMBOL_GPL(alloc_cc_shared_pages);
+
+void free_cc_shared_pages(struct cc_shared_pages *mem)
+{
+	if (!mem || !mem->page)
+		return;
+
+	if (!cc_platform_has(CC_ATTR_MEM_ENCRYPT))
+		goto free_pages;
+
+	if (cc_make_private(page_address(mem->page), mem->shared_size)) {
+		pr_warn_ratelimited("leaking %zu bytes that cannot be made private\n",
+				    mem->shared_size);
+		return;
+	}
+
+free_pages:
+	__free_pages(mem->page, get_order(mem->shared_size));
+	mem->page = NULL;
+	mem->shared_size = 0;
+}
+EXPORT_SYMBOL_GPL(free_cc_shared_pages);

3/2 2026/09/26 15:28 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 27097
							output: 38
							thoughts: 839
						

Thoughts:
**Analyzing the Patch Series: A Deep Dive**

Okay, let's break this down. We're looking at a patch series, and the target is amd64, which is a key point for context. The instructions imply a specific flow, so let's get into it.

First, I need to understand what's been touched. The diff affects a significant number of files, spanning several architectures and subsystems. I see that there are updates to `dma_alloc_from_contiguous` with an additional `attrs` argument in `arch/arm/mm/dma-mapping.c`. There's also `memset` in `arch/arm64/mm/pageattr.c`, and a wide variety of other files are touched too. The list looks long but it can be categorised logically. There's a lot of X86 related files. This includes hyperv, kvmclock and AMD memory encryption.

Now, ideally, I'd immediately consult the `git log` to grasp the patch series's purpose, but I need to think about my environment. I'll utilize the tools I have to inspect the source code to get the context. I'm seeing that I have tools like `grepper`, `codesearch-dir-index`, `read-file`, and `set-results`.

I'll start with `drivers/dma-buf/heaps/system_heap.c` because it jumps out at me. I'm seeing a section in `system_heap_allocate` where a check for `cc_shared` happens. There's a call to `cc_shared_calc_layout` and a related size calculation. This looks interesting... the code seems to be calculating a layout, possibly for some shared memory. This is particularly relevant given DMA and memory management contexts. I want to check how `cc_shared` is being set. It looks like it is allocating memory from the system heap, which is accessible from userspace. The `DMA_HEAP_IOCTL_ALLOC` ioctl on `/dev/dma_heap/system` confirms that.

So, I need to know *how* `priv->cc_shared` is being set in `drivers/dma-buf/heaps/system_heap.c`. I will utilize my tools to find this context. I will use the grep tool to search for instances of `cc_shared` within this file specifically to determine the conditions under which it's assigned. This should give me a better understanding of the data flow and the overall impact of the changes in the patch series.



4/2 2026/09/26 15:28 tool grepper
0m Args:
{
  "Expression": "cc_shared",
  "PathPrefix": "drivers/dma-buf/heaps/system_heap.c"
}

Results:
{
  "Output": "drivers/dma-buf/heaps/system_heap.c-13-#include \u003clinux/cc_platform.h\u003e\ndrivers/dma-buf/heaps/system_heap.c:14:#include \u003clinux/cc_shared.h\u003e\ndrivers/dma-buf/heaps/system_heap.c-15-#include \u003clinux/dma-buf.h\u003e\n--\ndrivers/dma-buf/heaps/system_heap.c=27=struct system_heap_priv {\ndrivers/dma-buf/heaps/system_heap.c:28:\tbool cc_shared;\ndrivers/dma-buf/heaps/system_heap.c-29-};\n--\ndrivers/dma-buf/heaps/system_heap.c=31=struct system_heap_buffer {\n--\ndrivers/dma-buf/heaps/system_heap.c-38-\tvoid *vaddr;\ndrivers/dma-buf/heaps/system_heap.c:39:\tbool cc_shared;\ndrivers/dma-buf/heaps/system_heap.c-40-};\n--\ndrivers/dma-buf/heaps/system_heap.c=42=struct dma_heap_attachment {\n--\ndrivers/dma-buf/heaps/system_heap.c-46-\tbool mapped;\ndrivers/dma-buf/heaps/system_heap.c:47:\tbool cc_shared;\ndrivers/dma-buf/heaps/system_heap.c-48-};\ndrivers/dma-buf/heaps/system_heap.c-49-\ndrivers/dma-buf/heaps/system_heap.c:50:#define cc_shared_buffer(b) (IS_ENABLED(CONFIG_DMABUF_HEAPS_SYSTEM_CC_SHARED) \u0026\u0026 \\\ndrivers/dma-buf/heaps/system_heap.c:51:\t\t\t\t(b)-\u003ecc_shared)\ndrivers/dma-buf/heaps/system_heap.c-52-\n--\ndrivers/dma-buf/heaps/system_heap.c=85=static int system_heap_attach(struct dma_buf *dmabuf,\n--\ndrivers/dma-buf/heaps/system_heap.c-104-\ta-\u003emapped = false;\ndrivers/dma-buf/heaps/system_heap.c:105:\ta-\u003ecc_shared = buffer-\u003ecc_shared;\ndrivers/dma-buf/heaps/system_heap.c-106-\n--\ndrivers/dma-buf/heaps/system_heap.c=130=static struct sg_table *system_heap_map_dma_buf(struct dma_buf_attachment *attachment,\n--\ndrivers/dma-buf/heaps/system_heap.c-137-\ndrivers/dma-buf/heaps/system_heap.c:138:\tattrs = cc_shared_buffer(a) ? DMA_ATTR_CC_SHARED : 0;\ndrivers/dma-buf/heaps/system_heap.c-139-\tret = dma_map_sgtable(attachment-\u003edev, table, direction, attrs);\n--\ndrivers/dma-buf/heaps/system_heap.c=199=static int system_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)\n--\ndrivers/dma-buf/heaps/system_heap.c-209-\tprot = vma-\u003evm_page_prot;\ndrivers/dma-buf/heaps/system_heap.c:210:\tif (cc_shared_buffer(buffer))\ndrivers/dma-buf/heaps/system_heap.c-211-\t\tprot = pgprot_decrypted(prot);\n--\ndrivers/dma-buf/heaps/system_heap.c=240=static void *system_heap_do_vmap(struct system_heap_buffer *buffer)\n--\ndrivers/dma-buf/heaps/system_heap.c-258-\tprot = PAGE_KERNEL;\ndrivers/dma-buf/heaps/system_heap.c:259:\tif (cc_shared_buffer(buffer))\ndrivers/dma-buf/heaps/system_heap.c-260-\t\tprot = pgprot_decrypted(prot);\n--\ndrivers/dma-buf/heaps/system_heap.c=298=static void system_heap_vunmap(struct dma_buf *dmabuf, struct iosys_map *map)\n--\ndrivers/dma-buf/heaps/system_heap.c-310-\ndrivers/dma-buf/heaps/system_heap.c:311:static void system_heap_free_page(struct page *page, bool cc_shared)\ndrivers/dma-buf/heaps/system_heap.c-312-{\ndrivers/dma-buf/heaps/system_heap.c:313:\tstruct cc_shared_pages mem;\ndrivers/dma-buf/heaps/system_heap.c-314-\ndrivers/dma-buf/heaps/system_heap.c:315:\tif (!cc_shared) {\ndrivers/dma-buf/heaps/system_heap.c-316-\t\t__free_pages(page, compound_order(page));\n--\ndrivers/dma-buf/heaps/system_heap.c-321-\tmem.shared_size = page_size(page);\ndrivers/dma-buf/heaps/system_heap.c:322:\tfree_cc_shared_pages(\u0026mem);\ndrivers/dma-buf/heaps/system_heap.c-323-}\n--\ndrivers/dma-buf/heaps/system_heap.c=325=static void system_heap_dma_buf_release(struct dma_buf *dmabuf)\n--\ndrivers/dma-buf/heaps/system_heap.c-333-\tfor_each_sgtable_sg(table, sg, i)\ndrivers/dma-buf/heaps/system_heap.c:334:\t\tsystem_heap_free_page(sg_page(sg), cc_shared_buffer(buffer));\ndrivers/dma-buf/heaps/system_heap.c-335-\tsg_free_table(table);\n--\ndrivers/dma-buf/heaps/system_heap.c=352=static struct page *system_heap_alloc_order(unsigned int order,\ndrivers/dma-buf/heaps/system_heap.c:353:\t     gfp_t flags, bool cc_shared)\ndrivers/dma-buf/heaps/system_heap.c-354-{\ndrivers/dma-buf/heaps/system_heap.c:355:\tstruct cc_shared_pages mem;\ndrivers/dma-buf/heaps/system_heap.c-356-\ndrivers/dma-buf/heaps/system_heap.c:357:\tif (!cc_shared)\ndrivers/dma-buf/heaps/system_heap.c-358-\t\treturn alloc_pages(flags, order);\n--\ndrivers/dma-buf/heaps/system_heap.c-361-\tflags |= __GFP_COMP;\ndrivers/dma-buf/heaps/system_heap.c:362:\tif (alloc_cc_shared_pages(flags, PAGE_SIZE \u003c\u003c order, \u0026mem))\ndrivers/dma-buf/heaps/system_heap.c-363-\t\treturn NULL;\n--\ndrivers/dma-buf/heaps/system_heap.c=368=static struct page *alloc_largest_available(unsigned long size,\ndrivers/dma-buf/heaps/system_heap.c:369:\t\tunsigned int max_order, bool cc_shared)\ndrivers/dma-buf/heaps/system_heap.c-370-{\n--\ndrivers/dma-buf/heaps/system_heap.c-383-\t\t\tflags |= __GFP_ACCOUNT;\ndrivers/dma-buf/heaps/system_heap.c:384:\t\tpage = system_heap_alloc_order(orders[i], flags, cc_shared);\ndrivers/dma-buf/heaps/system_heap.c-385-\t\tif (!page)\n--\ndrivers/dma-buf/heaps/system_heap.c=392=static struct dma_buf *system_heap_allocate(struct dma_heap *heap,\n--\ndrivers/dma-buf/heaps/system_heap.c-398-\tDEFINE_DMA_BUF_EXPORT_INFO(exp_info);\ndrivers/dma-buf/heaps/system_heap.c:399:\tstruct cc_shared_layout layout;\ndrivers/dma-buf/heaps/system_heap.c-400-\tunsigned long size_remaining = len;\n--\ndrivers/dma-buf/heaps/system_heap.c-403-\tstruct system_heap_priv *priv = dma_heap_get_drvdata(heap);\ndrivers/dma-buf/heaps/system_heap.c:404:\tbool cc_shared = priv-\u003ecc_shared;\ndrivers/dma-buf/heaps/system_heap.c-405-\tstruct dma_buf *dmabuf;\n--\ndrivers/dma-buf/heaps/system_heap.c-411-\ndrivers/dma-buf/heaps/system_heap.c:412:\tif (cc_shared) {\ndrivers/dma-buf/heaps/system_heap.c-413-\t\tint err;\ndrivers/dma-buf/heaps/system_heap.c-414-\ndrivers/dma-buf/heaps/system_heap.c:415:\t\terr = cc_shared_calc_layout(len, \u0026layout);\ndrivers/dma-buf/heaps/system_heap.c-416-\t\tif (err)\n--\ndrivers/dma-buf/heaps/system_heap.c-429-\tbuffer-\u003elen = len;\ndrivers/dma-buf/heaps/system_heap.c:430:\tbuffer-\u003ecc_shared = cc_shared;\ndrivers/dma-buf/heaps/system_heap.c-431-\n--\ndrivers/dma-buf/heaps/system_heap.c-444-\t\tpage = alloc_largest_available(size_remaining, max_order,\ndrivers/dma-buf/heaps/system_heap.c:445:\t\t\t\t\t       cc_shared);\ndrivers/dma-buf/heaps/system_heap.c-446-\t\tif (!page)\n--\ndrivers/dma-buf/heaps/system_heap.c-484-\tfor_each_sgtable_sg(table, sg, i)\ndrivers/dma-buf/heaps/system_heap.c:485:\t\tsystem_heap_free_page(sg_page(sg), cc_shared);\ndrivers/dma-buf/heaps/system_heap.c-486-\tsg_free_table(table);\n--\ndrivers/dma-buf/heaps/system_heap.c-488-\tlist_for_each_entry_safe(page, tmp_page, \u0026pages, lru)\ndrivers/dma-buf/heaps/system_heap.c:489:\t\tsystem_heap_free_page(page, cc_shared);\ndrivers/dma-buf/heaps/system_heap.c-490-\tkfree(buffer);\n--\ndrivers/dma-buf/heaps/system_heap.c=499=static struct system_heap_priv system_heap_priv = {\ndrivers/dma-buf/heaps/system_heap.c:500:\t.cc_shared = false,\ndrivers/dma-buf/heaps/system_heap.c-501-};\ndrivers/dma-buf/heaps/system_heap.c-502-\ndrivers/dma-buf/heaps/system_heap.c:503:static struct system_heap_priv system_heap_cc_shared_priv = {\ndrivers/dma-buf/heaps/system_heap.c:504:\t.cc_shared = true,\ndrivers/dma-buf/heaps/system_heap.c-505-};\n--\ndrivers/dma-buf/heaps/system_heap.c=507=static int __init system_heap_create(void)\n--\ndrivers/dma-buf/heaps/system_heap.c-524-\ndrivers/dma-buf/heaps/system_heap.c:525:\texp_info.name = \"system_cc_shared\";\ndrivers/dma-buf/heaps/system_heap.c:526:\texp_info.priv = \u0026system_heap_cc_shared_priv;\ndrivers/dma-buf/heaps/system_heap.c-527-\tsys_heap = dma_heap_add(\u0026exp_info);\n"
}

5/2 2026/09/26 15:28 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 31768
							output: 43
							thoughts: 102
						

Thoughts:
Okay, let's dive into this. Here's my take on those code snippets:

**Diving into System DMA Heap Creation**

Alright, so I'm seeing that `drivers/dma-buf/heaps/system_heap.c` adds a `/dev/dma_heap/system_cc_shared` device node. That's interesting, especially when `CONFIG_DMABUF_HEAPS_SYSTEM_CC_SHARED` (or a similar configuration option) is active. My initial thought is that this likely relates to some form of shared memory pool managed via DMA for system-level communications, possibly involving a cross-component (CC) sharing mechanism. The naming convention certainly hints at that. I'll need to confirm, though. Let's dig deeper into the code to confirm my assumptions. Looking at lines 500-540 in `drivers/dma-buf/heaps/system_heap.c` sounds like a good starting point to identify the relevant logic. I'm expecting to find the device node registration or some key initialization related to this shared heap within that section. Hopefully the code is well-commented. I'll also look for references to any critical section to ensure thread safety when using this pool.



6/2 2026/09/26 15:28 tool read-file
0m Args:
{
  "File": "drivers/dma-buf/heaps/system_heap.c",
  "FirstLine": 495,
  "LineCount": 45
}

Results:
{
  "Contents": " 495:\tstatic const struct dma_heap_ops system_heap_ops = {\n 496:\t\t.allocate = system_heap_allocate,\n 497:\t};\n 498:\t\n 499:\tstatic struct system_heap_priv system_heap_priv = {\n 500:\t\t.cc_shared = false,\n 501:\t};\n 502:\t\n 503:\tstatic struct system_heap_priv system_heap_cc_shared_priv = {\n 504:\t\t.cc_shared = true,\n 505:\t};\n 506:\t\n 507:\tstatic int __init system_heap_create(void)\n 508:\t{\n 509:\t\tstruct dma_heap_export_info exp_info;\n 510:\t\tstruct dma_heap *sys_heap;\n 511:\t\n 512:\t\texp_info.name = \"system\";\n 513:\t\texp_info.ops = \u0026system_heap_ops;\n 514:\t\texp_info.priv = \u0026system_heap_priv;\n 515:\t\n 516:\t\tsys_heap = dma_heap_add(\u0026exp_info);\n 517:\t\tif (IS_ERR(sys_heap))\n 518:\t\t\treturn PTR_ERR(sys_heap);\n 519:\t\n 520:\t\tif (IS_ENABLED(CONFIG_HIGHMEM) ||\n 521:\t\t    !IS_ENABLED(CONFIG_DMABUF_HEAPS_SYSTEM_CC_SHARED) ||\n 522:\t\t    !cc_platform_has(CC_ATTR_MEM_ENCRYPT))\n 523:\t\t\treturn 0;\n 524:\t\n 525:\t\texp_info.name = \"system_cc_shared\";\n 526:\t\texp_info.priv = \u0026system_heap_cc_shared_priv;\n 527:\t\tsys_heap = dma_heap_add(\u0026exp_info);\n 528:\t\tif (IS_ERR(sys_heap))\n 529:\t\t\treturn PTR_ERR(sys_heap);\n 530:\t\n 531:\t\treturn 0;\n 532:\t}\n 533:\tmodule_init(system_heap_create);\n 534:\t\n 535:\tMODULE_DESCRIPTION(\"DMA-BUF System Heap\");\n 536:\tMODULE_LICENSE(\"GPL\");\n 537:\tMODULE_IMPORT_NS(\"DMA_BUF\");\n 538:\tMODULE_IMPORT_NS(\"DMA_BUF_HEAP\");\n"
}

7/2 2026/09/26 15:28 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 32853
							output: 24
							thoughts: 2848
						

Thoughts:
**DMA, Memory Encryption, and Shared Memory Analysis**

Okay, so I've been digging into this code, specifically around DMA allocations and memory encryption, it seems. The initial point of interest was line 522, specifically the `!cc_platform_has(CC_ATTR_MEM_ENCRYPT)` check. That triggered a deeper dive into how this `cc_platform_has` function is used. The key area is where DMA is managed, particularly the changes in `kernel/dma/direct.c`.

The flag `__DMA_ATTR_ALLOC_CC_SHARED` seems central. It influences whether memory is allocated as "unencrypted/shared DMA." This is because, if true, there is no need to make the memory encrypted. The code is being made to change the way that `dma_direct_alloc()` and related functions deal with this flag.

I noticed that `system_heap_allocate` calls `alloc_largest_available`, and eventually `system_heap_alloc_order`, and those functions are now receiving this `cc_shared` parameter, which is a boolean, and this, in turn, influences the allocation path. In the case where `cc_shared` is false, it uses regular page allocation.

Critically, the new code in `dma_direct_alloc` and the new function `dma_direct_free` now use these structures, and they are conditional on `attrs & __DMA_ATTR_ALLOC_CC_SHARED`. When set, the code is using `cc_shared_calc_layout`, suggesting a new layout or memory organization scheme is in play, perhaps related to sharing or encryption. There's also some interesting logic to handle zeroing of memory based on the `mark_mem_decrypt` flag. The initial zeroing of memory is now conditional, which implies it's handled differently depending on if the memory is "shared" or encrypted or not.

I'm starting to see a pattern: the system is making an effort to share DMA memory between different entities. So, drivers like virtio, usb, sound, net, etc., call `dma_alloc_coherent`. And it seems that the memory can be either "shared" or private, and both allocation and free need to have this taken into account. I think that the point of these changes is a new DMA management scheme based on some kind of "shared" memory, and this shared memory has a new layout managed by `mm/cc_shared.c`, which, according to the Makefile, is now unconditionally included in the kernel. This new module seems to be the core of the shared memory management.

I need to read the commit messages and related code to find what are the exact goals and why the system changed the DMA allocation scheme. I need to grep for `__DMA_ATTR_ALLOC_CC_SHARED` to get more context about the changes.



8/2 2026/09/26 15:28 tool grepper
0m Args:
{
  "Expression": "__DMA_ATTR_ALLOC_CC_SHARED"
}

Results:
{
  "Output": "Documentation/core-api/dma-attributes.rst=195=DMA_ATTR_MMIO is provided, the mapping requires a struct page.\nDocumentation/core-api/dma-attributes.rst-196-\nDocumentation/core-api/dma-attributes.rst:197:__DMA_ATTR_ALLOC_CC_SHARED\nDocumentation/core-api/dma-attributes.rst-198---------------------------\n--\nDocumentation/core-api/dma-attributes.rst=205=restore encryption on free.\nDocumentation/core-api/dma-attributes.rst-206-\nDocumentation/core-api/dma-attributes.rst:207:__DMA_ATTR_ALLOC_CC_SHARED differs from DMA_ATTR_CC_SHARED in that it is not\nDocumentation/core-api/dma-attributes.rst-208-a caller-visible DMA API attribute. DMA_ATTR_CC_SHARED describes an\n--\ndrivers/iommu/dma-iommu.c=1658=void *iommu_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,\n--\ndrivers/iommu/dma-iommu.c-1662-\tint ioprot = dma_info_to_prot(DMA_BIDIRECTIONAL, coherent, attrs);\ndrivers/iommu/dma-iommu.c:1663:\tbool is_alloc_cc_shared = attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED;\ndrivers/iommu/dma-iommu.c-1664-\tstruct page *page = NULL;\n--\ninclude/linux/dma-mapping.h-106-/*\ninclude/linux/dma-mapping.h:107: * __DMA_ATTR_ALLOC_CC_SHARED: Internal DMA-mapping attribute used by\ninclude/linux/dma-mapping.h-108- * allocation paths that create shared (decrypted) backing pages for\n--\ninclude/linux/dma-mapping.h-111- */\ninclude/linux/dma-mapping.h:112:#define __DMA_ATTR_ALLOC_CC_SHARED\t(1UL \u003c\u003c 14)\ninclude/linux/dma-mapping.h-113-\n--\ninclude/trace/events/dma.h=16=TRACE_DEFINE_ENUM(DMA_NONE);\n--\ninclude/trace/events/dma.h-38-\t\t{ DMA_ATTR_CC_SHARED, \"CC_SHARED\" }, \\\ninclude/trace/events/dma.h:39:\t\t{ __DMA_ATTR_ALLOC_CC_SHARED, \"ALLOC_CC_SHARED\" })\ninclude/trace/events/dma.h-40-\n--\nkernel/dma/contiguous.c=374=struct page *dma_alloc_from_contiguous(struct device *dev, size_t count,\n--\nkernel/dma/contiguous.c-378-\nkernel/dma/contiguous.c:379:\tif (attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED)\nkernel/dma/contiguous.c-380-\t\trequired_align = get_order(cc_shared_granule_size());\n--\nkernel/dma/contiguous.c=426=struct page *dma_alloc_contiguous(struct device *dev, size_t size,\n--\nkernel/dma/contiguous.c-437-\t\treturn NULL;\nkernel/dma/contiguous.c:438:\tif (attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED)\nkernel/dma/contiguous.c-439-\t\trequired_align = get_order(cc_shared_granule_size());\n--\nkernel/dma/direct.c=162=static struct page *dma_direct_alloc_from_pool(struct device *dev, size_t size,\n--\nkernel/dma/direct.c-177-\t*dma_handle = phys_to_dma_direct(dev, page_to_phys(page),\nkernel/dma/direct.c:178:\t\t\t\t\t attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED);\nkernel/dma/direct.c-179-\treturn page;\n--\nkernel/dma/direct.c=202=void *dma_direct_alloc(struct device *dev, size_t size,\n--\nkernel/dma/direct.c-212-\tif (force_dma_unencrypted(dev))\nkernel/dma/direct.c:213:\t\tattrs |= __DMA_ATTR_ALLOC_CC_SHARED;\nkernel/dma/direct.c-214-\nkernel/dma/direct.c:215:\tmark_mem_decrypt = attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED;\nkernel/dma/direct.c-216-\tif (mark_mem_decrypt) {\n--\nkernel/dma/direct.c-229-\nkernel/dma/direct.c:230:\tif (((attrs \u0026 (DMA_ATTR_NO_KERNEL_MAPPING | __DMA_ATTR_ALLOC_CC_SHARED)) ==\nkernel/dma/direct.c-231-\t     DMA_ATTR_NO_KERNEL_MAPPING) \u0026\u0026 !is_swiotlb_for_alloc(dev))\n--\nkernel/dma/direct.c-267-\t */\nkernel/dma/direct.c:268:\tif ((remap || (attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED)) \u0026\u0026\nkernel/dma/direct.c-269-\t    dma_direct_use_pool(dev, gfp)) {\n--\nkernel/dma/direct.c-349-\t*dma_handle = phys_to_dma_direct(dev, page_to_phys(page),\nkernel/dma/direct.c:350:\t\t\t\t\t attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED);\nkernel/dma/direct.c-351-\treturn cpu_addr;\n--\nkernel/dma/direct.c=369=void dma_direct_free(struct device *dev, size_t size,\n--\nkernel/dma/direct.c-382-\tif (force_dma_unencrypted(dev))\nkernel/dma/direct.c:383:\t\tattrs |= __DMA_ATTR_ALLOC_CC_SHARED;\nkernel/dma/direct.c-384-\nkernel/dma/direct.c:385:\tif (attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED)\nkernel/dma/direct.c-386-\t\tmark_mem_encrypted = true;\nkernel/dma/direct.c-387-\nkernel/dma/direct.c:388:\tif (((attrs \u0026 (DMA_ATTR_NO_KERNEL_MAPPING | __DMA_ATTR_ALLOC_CC_SHARED)) ==\nkernel/dma/direct.c-389-\t     DMA_ATTR_NO_KERNEL_MAPPING) \u0026\u0026 !is_swiotlb_for_alloc(dev)) {\n--\nkernel/dma/direct.c=447=struct page *dma_direct_alloc_pages(struct device *dev, size_t size,\n--\nkernel/dma/direct.c-456-\tif (force_dma_unencrypted(dev))\nkernel/dma/direct.c:457:\t\tattrs |= __DMA_ATTR_ALLOC_CC_SHARED;\nkernel/dma/direct.c-458-\nkernel/dma/direct.c:459:\tmark_mem_decrypt = attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED;\nkernel/dma/direct.c-460-\nkernel/dma/direct.c:461:\tif ((attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED) \u0026\u0026 dma_direct_use_pool(dev, gfp))\nkernel/dma/direct.c-462-\t\treturn dma_direct_alloc_from_pool(dev, size, dma_handle,\n--\nkernel/dma/direct.c-493-\t*dma_handle = phys_to_dma_direct(dev, page_to_phys(page),\nkernel/dma/direct.c:494:\t\t\t\t\t attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED);\nkernel/dma/direct.c-495-\treturn page;\n--\nkernel/dma/mapping.c=538=pgprot_t dma_pgprot(struct device *dev, pgprot_t prot, unsigned long attrs)\n--\nkernel/dma/mapping.c-550-\nkernel/dma/mapping.c:551:\tif (attrs \u0026 (DMA_ATTR_CC_SHARED | __DMA_ATTR_ALLOC_CC_SHARED))\nkernel/dma/mapping.c-552-\t\treturn pgprot_decrypted(dma_prot);\n--\nkernel/dma/mapping.c=633=void *dma_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle,\n--\nkernel/dma/mapping.c-648-\nkernel/dma/mapping.c:649:\tif (attrs \u0026 (DMA_ATTR_CC_SHARED | __DMA_ATTR_ALLOC_CC_SHARED)) {\nkernel/dma/mapping.c-650-\t\ttrace_dma_alloc(dev, NULL, 0, size, DMA_BIDIRECTIONAL, flag,\n--\nkernel/dma/mapping.c-655-\tif (force_dma_unencrypted(dev))\nkernel/dma/mapping.c:656:\t\tattrs |= __DMA_ATTR_ALLOC_CC_SHARED;\nkernel/dma/mapping.c-657-\n--\nkernel/dma/pool.c=85=static int atomic_pool_expand(struct dma_gen_pool *dma_pool, size_t pool_size,\n--\nkernel/dma/pool.c-105-\t\t\treturn -E2BIG;\nkernel/dma/pool.c:106:\t\tattrs = __DMA_ATTR_ALLOC_CC_SHARED;\nkernel/dma/pool.c-107-\t}\n--\nkernel/dma/pool.c=335=struct page *dma_alloc_from_pool(struct device *dev, size_t size,\n--\nkernel/dma/pool.c-344-\nkernel/dma/pool.c:345:\t\tif (dma_pool-\u003ecc_shared != !!(attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED))\nkernel/dma/pool.c-346-\t\t\tcontinue;\n--\nkernel/dma/swiotlb.c=724=static struct page *alloc_dma_pages(gfp_t gfp, size_t bytes,\n--\nkernel/dma/swiotlb.c-726-{\nkernel/dma/swiotlb.c:727:\tbool cc_shared = attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED;\nkernel/dma/swiotlb.c-728-\tunsigned int order;\n--\nkernel/dma/swiotlb.c=769=static struct page *swiotlb_alloc_tlb(struct device *dev,\n--\nkernel/dma/swiotlb.c-773-\tstruct page *page;\nkernel/dma/swiotlb.c:774:\tunsigned long attrs = mem-\u003ecc_shared ? __DMA_ATTR_ALLOC_CC_SHARED : 0;\nkernel/dma/swiotlb.c-775-\n--\nkernel/dma/swiotlb.c=1965=struct page *swiotlb_alloc(struct device *dev, size_t size, unsigned long attrs)\n--\nkernel/dma/swiotlb.c-1975-\nkernel/dma/swiotlb.c:1976:\tif (mem-\u003ecc_shared != !!(attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED))\nkernel/dma/swiotlb.c-1977-\t\treturn NULL;\n"
}

9/2 2026/09/26 15:28 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 39303
							output: 22
							thoughts: 365
						

Thoughts:
**Analyzing DMA Heap Allocation and Potential Security Implications**

Okay, let's dive into these recent kernel changes, specifically focusing on the DMA-related files. I need to understand what's been updated in `kernel/dma/contiguous.c`, `kernel/dma/direct.c`, and particularly, the heap implementations like `drivers/dma-buf/heaps/system_heap.c`, and also `mm/cc_shared.c` as it seems related.

Let's focus on `system_heap.c`. When a process opens `/dev/dma_heap/system` and requests memory via `DMA_HEAP_IOCTL_ALLOC`, it calls `system_heap_allocate`. Ah, here we go, the conditional check: if `cc_shared` is true, the code calls `cc_shared_calc_layout` which then adjusts `size_remaining`. This warrants a closer look...

Now, where is `cc_shared` initialized? Let's trace this back to `system_heap_create`. The crucial bit: the heap's creation is conditional! It *only* happens if `CONFIG_HIGHMEM` isn't enabled, `CONFIG_DMABUF_HEAPS_SYSTEM_CC_SHARED` *is* enabled, and crucially, `cc_platform_has(CC_ATTR_MEM_ENCRYPT)` returns true. The heap is named "system_cc_shared". This looks important.

Okay, so what *is* `cc_platform_has(CC_ATTR_MEM_ENCRYPT)`? That needs immediate clarification. I need to find all instances of `CC_ATTR_MEM_ENCRYPT` to see how this memory encryption attribute is being used. This could be a security-related feature and I want to understand how it's being implemented, and where.



10/2 2026/09/26 15:29 tool grepper
0m Args:
{
  "Expression": "CC_ATTR_MEM_ENCRYPT"
}

Results:
{
  "Output": "arch/arm64/mm/init.c=416=bool cc_platform_has(enum cc_attr attr)\n--\narch/arm64/mm/init.c-418-\tswitch (attr) {\narch/arm64/mm/init.c:419:\tcase CC_ATTR_MEM_ENCRYPT:\narch/arm64/mm/init.c-420-\tcase CC_ATTR_GUEST_MEM_ENCRYPT:\n--\narch/powerpc/platforms/pseries/cc_platform.c=16=bool cc_platform_has(enum cc_attr attr)\n--\narch/powerpc/platforms/pseries/cc_platform.c-18-\tswitch (attr) {\narch/powerpc/platforms/pseries/cc_platform.c:19:\tcase CC_ATTR_MEM_ENCRYPT:\narch/powerpc/platforms/pseries/cc_platform.c-20-\tcase CC_ATTR_GUEST_MEM_ENCRYPT:\n--\narch/powerpc/platforms/pseries/svm.c=42=int set_memory_encrypted(unsigned long addr, int numpages)\narch/powerpc/platforms/pseries/svm.c-43-{\narch/powerpc/platforms/pseries/svm.c:44:\tif (!cc_platform_has(CC_ATTR_MEM_ENCRYPT))\narch/powerpc/platforms/pseries/svm.c-45-\t\treturn 0;\n--\narch/powerpc/platforms/pseries/svm.c=55=int set_memory_decrypted(unsigned long addr, int numpages)\narch/powerpc/platforms/pseries/svm.c-56-{\narch/powerpc/platforms/pseries/svm.c:57:\tif (!cc_platform_has(CC_ATTR_MEM_ENCRYPT))\narch/powerpc/platforms/pseries/svm.c-58-\t\treturn 0;\n--\narch/s390/mm/init.c=150=bool cc_platform_has(enum cc_attr attr)\n--\narch/s390/mm/init.c-152-\tswitch (attr) {\narch/s390/mm/init.c:153:\tcase CC_ATTR_MEM_ENCRYPT:\narch/s390/mm/init.c-154-\tcase CC_ATTR_GUEST_MEM_ENCRYPT:\n--\narch/x86/coco/core.c=30=static bool noinstr intel_cc_platform_has(enum cc_attr attr)\n--\narch/x86/coco/core.c-34-\tcase CC_ATTR_GUEST_MEM_ENCRYPT:\narch/x86/coco/core.c:35:\tcase CC_ATTR_MEM_ENCRYPT:\narch/x86/coco/core.c-36-\t\treturn true;\n--\narch/x86/coco/core.c=47=static __maybe_unused __always_inline bool amd_cc_platform_vtom(enum cc_attr attr)\n--\narch/x86/coco/core.c-50-\tcase CC_ATTR_GUEST_MEM_ENCRYPT:\narch/x86/coco/core.c:51:\tcase CC_ATTR_MEM_ENCRYPT:\narch/x86/coco/core.c-52-\t\treturn true;\n--\narch/x86/coco/core.c-61- * cc_platform_has() function is used for this.  When a distinction isn't\narch/x86/coco/core.c:62: * needed, the CC_ATTR_MEM_ENCRYPT attribute can be used.\narch/x86/coco/core.c-63- *\n--\narch/x86/coco/core.c=70=static bool noinstr amd_cc_platform_has(enum cc_attr attr)\n--\narch/x86/coco/core.c-77-\tswitch (attr) {\narch/x86/coco/core.c:78:\tcase CC_ATTR_MEM_ENCRYPT:\narch/x86/coco/core.c-79-\t\treturn sme_me_mask;\n--\narch/x86/include/asm/vmware.h=228=unsigned long vmware_hypercall7(unsigned long cmd, unsigned long in1,\n--\narch/x86/include/asm/vmware.h-263- * High bandwidth calls are not supported on encrypted memory guests.\narch/x86/include/asm/vmware.h:264: * The caller should check cc_platform_has(CC_ATTR_MEM_ENCRYPT) and use\narch/x86/include/asm/vmware.h-265- * low bandwidth hypercall if memory encryption is set.\n--\narch/x86/mm/ioremap.c=729=bool arch_memremap_can_ram_remap(resource_size_t phys_addr, unsigned long size,\n--\narch/x86/mm/ioremap.c-731-{\narch/x86/mm/ioremap.c:732:\tif (!cc_platform_has(CC_ATTR_MEM_ENCRYPT))\narch/x86/mm/ioremap.c-733-\t\treturn true;\n--\narch/x86/mm/ioremap.c=756=pgprot_t __init early_memremap_pgprot_adjust(resource_size_t phys_addr,\n--\narch/x86/mm/ioremap.c-761-\narch/x86/mm/ioremap.c:762:\tif (!cc_platform_has(CC_ATTR_MEM_ENCRYPT))\narch/x86/mm/ioremap.c-763-\t\treturn prot;\n--\narch/x86/mm/mem_encrypt.c=89=void __init mem_encrypt_init(void)\narch/x86/mm/mem_encrypt.c-90-{\narch/x86/mm/mem_encrypt.c:91:\tif (!cc_platform_has(CC_ATTR_MEM_ENCRYPT))\narch/x86/mm/mem_encrypt.c-92-\t\treturn;\n--\narch/x86/mm/mem_encrypt_amd.c=547=void __init mem_encrypt_free_decrypted_mem(void)\n--\narch/x86/mm/mem_encrypt_amd.c-560-\t * sme_postprocess_startup(). Higher level abstractions, such as\narch/x86/mm/mem_encrypt_amd.c:561:\t * CC_ATTR_MEM_ENCRYPT, aren't necessarily equivalent in a Hyper-V VM\narch/x86/mm/mem_encrypt_amd.c-562-\t * using vTOM, where sme_me_mask is always zero.\n--\narch/x86/mm/pat/set_memory.c=2509=static int __set_memory_enc_dec(unsigned long addr, int numpages, bool enc)\n--\narch/x86/mm/pat/set_memory.c-2512-\narch/x86/mm/pat/set_memory.c:2513:\tif (cc_platform_has(CC_ATTR_MEM_ENCRYPT)) {\narch/x86/mm/pat/set_memory.c-2514-\t\tif (!down_read_trylock(\u0026mem_enc_lock))\n--\ndrivers/dma-buf/heaps/system_heap.c=507=static int __init system_heap_create(void)\n--\ndrivers/dma-buf/heaps/system_heap.c-521-\t    !IS_ENABLED(CONFIG_DMABUF_HEAPS_SYSTEM_CC_SHARED) ||\ndrivers/dma-buf/heaps/system_heap.c:522:\t    !cc_platform_has(CC_ATTR_MEM_ENCRYPT))\ndrivers/dma-buf/heaps/system_heap.c-523-\t\treturn 0;\n--\ndrivers/gpu/drm/amd/amdgpu/amdgpu_drv.c=2386=static int amdgpu_pci_probe(struct pci_dev *pdev,\n--\ndrivers/gpu/drm/amd/amdgpu/amdgpu_drv.c-2422-\t */\ndrivers/gpu/drm/amd/amdgpu/amdgpu_drv.c:2423:\tif (cc_platform_has(CC_ATTR_MEM_ENCRYPT) \u0026\u0026\ndrivers/gpu/drm/amd/amdgpu/amdgpu_drv.c-2424-\t    ((flags \u0026 AMD_ASIC_MASK) == CHIP_RAVEN)) {\n--\ndrivers/gpu/drm/drm_cache.c=180=bool drm_need_swiotlb(int dma_bits)\n--\ndrivers/gpu/drm/drm_cache.c-200-\t */\ndrivers/gpu/drm/drm_cache.c:201:\tif (cc_platform_has(CC_ATTR_MEM_ENCRYPT))\ndrivers/gpu/drm/drm_cache.c-202-\t\treturn true;\n--\ndrivers/gpu/drm/vmwgfx/vmwgfx_drv.c=645=static int vmw_dma_select_mode(struct vmw_private *dev_priv)\n--\ndrivers/gpu/drm/vmwgfx/vmwgfx_drv.c-656-\t */\ndrivers/gpu/drm/vmwgfx/vmwgfx_drv.c:657:\tif (vmw_force_coherent || cc_platform_has(CC_ATTR_MEM_ENCRYPT))\ndrivers/gpu/drm/vmwgfx/vmwgfx_drv.c-658-\t\tdev_priv-\u003emap_mode = vmw_dma_alloc_coherent;\n--\ndrivers/gpu/drm/vmwgfx/vmwgfx_msg.c=155=static unsigned long vmw_port_hb_out(struct rpc_channel *channel,\n--\ndrivers/gpu/drm/vmwgfx/vmwgfx_msg.c-161-\t/* HB port can't access encrypted memory. */\ndrivers/gpu/drm/vmwgfx/vmwgfx_msg.c:162:\tif (hb \u0026\u0026 !cc_platform_has(CC_ATTR_MEM_ENCRYPT)) {\ndrivers/gpu/drm/vmwgfx/vmwgfx_msg.c-163-\t\tvmware_hypercall_hb_out(\n--\ndrivers/gpu/drm/vmwgfx/vmwgfx_msg.c=205=static unsigned long vmw_port_hb_in(struct rpc_channel *channel, char *reply,\n--\ndrivers/gpu/drm/vmwgfx/vmwgfx_msg.c-210-\t/* HB port can't access encrypted memory */\ndrivers/gpu/drm/vmwgfx/vmwgfx_msg.c:211:\tif (hb \u0026\u0026 !cc_platform_has(CC_ATTR_MEM_ENCRYPT)) {\ndrivers/gpu/drm/vmwgfx/vmwgfx_msg.c-212-\t\tvmware_hypercall_hb_in(\n--\ndrivers/iommu/amd/iommu.c=3165=static int amd_iommu_def_domain_type(struct device *dev)\n--\ndrivers/iommu/amd/iommu.c-3184-\tif (pdev_pasid_supported(dev_data) \u0026\u0026\ndrivers/iommu/amd/iommu.c:3185:\t    !cc_platform_has(CC_ATTR_MEM_ENCRYPT) \u0026\u0026\ndrivers/iommu/amd/iommu.c-3186-\t    !amd_iommu_snp_en) {\n--\ndrivers/iommu/iommu.c=228=static int __init iommu_subsys_init(void)\n--\ndrivers/iommu/iommu.c-237-\ndrivers/iommu/iommu.c:238:\t\tif (iommu_default_passthrough() \u0026\u0026 cc_platform_has(CC_ATTR_MEM_ENCRYPT)) {\ndrivers/iommu/iommu.c-239-\t\t\tpr_info(\"Memory encryption detected - Disabling default IOMMU Passthrough\\n\");\n--\nfs/proc/vmcore.c=238=ssize_t __weak elfcorehdr_read_notes(char *buf, size_t count, u64 *ppos)\n--\nfs/proc/vmcore.c-245-\treturn read_from_oldmem(\u0026iter, count, ppos,\nfs/proc/vmcore.c:246:\t\t\tcc_platform_has(CC_ATTR_MEM_ENCRYPT));\nfs/proc/vmcore.c-247-}\n--\nfs/proc/vmcore.c=333=static ssize_t __read_vmcore(struct iov_iter *iter, loff_t *fpos)\n--\nfs/proc/vmcore.c-412-\t\t\ttmp = read_from_oldmem(iter, tsz, \u0026start,\nfs/proc/vmcore.c:413:\t\t\t\t\tcc_platform_has(CC_ATTR_MEM_ENCRYPT));\nfs/proc/vmcore.c-414-\t\t\tif (tmp \u003c 0)\n--\ninclude/linux/cc_platform.h=22=enum cc_attr {\ninclude/linux/cc_platform.h-23-\t/**\ninclude/linux/cc_platform.h:24:\t * @CC_ATTR_MEM_ENCRYPT: Memory encryption is active\ninclude/linux/cc_platform.h-25-\t *\n--\ninclude/linux/cc_platform.h-32-\t */\ninclude/linux/cc_platform.h:33:\tCC_ATTR_MEM_ENCRYPT,\ninclude/linux/cc_platform.h-34-\n--\nkernel/crash_dump_dm_crypt.c=55=ssize_t __weak dm_crypt_keys_read(char *buf, size_t count, u64 *ppos)\n--\nkernel/crash_dump_dm_crypt.c-60-\tiov_iter_kvec(\u0026iter, READ, \u0026kvec, 1, count);\nkernel/crash_dump_dm_crypt.c:61:\treturn read_from_oldmem(\u0026iter, count, ppos, cc_platform_has(CC_ATTR_MEM_ENCRYPT));\nkernel/crash_dump_dm_crypt.c-62-}\n--\nkernel/dma/pool.c=191=static __init struct dma_gen_pool *__dma_atomic_pool_init(struct dma_gen_pool *dma_pool,\n--\nkernel/dma/pool.c-202-\t/* if platform is using memory encryption atomic pools are by default shared. */\nkernel/dma/pool.c:203:\tif (cc_platform_has(CC_ATTR_MEM_ENCRYPT))\nkernel/dma/pool.c-204-\t\tdma_pool-\u003ecc_shared = true;\n--\nkernel/dma/swiotlb.c=364=void __init swiotlb_update_mem_attributes(void)\n--\nkernel/dma/swiotlb.c-372-\t */\nkernel/dma/swiotlb.c:373:\tif (cc_platform_has(CC_ATTR_MEM_ENCRYPT))\nkernel/dma/swiotlb.c-374-\t\tio_tlb_default_mem.cc_shared = true;\n--\nkernel/dma/swiotlb.c=1554=phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr,\n--\nkernel/dma/swiotlb.c-1572-\nkernel/dma/swiotlb.c:1573:\tif (cc_platform_has(CC_ATTR_MEM_ENCRYPT))\nkernel/dma/swiotlb.c-1574-\t\tpr_warn_once(\"Memory encryption is active and system is using DMA bounce buffers\\n\");\n--\nkernel/dma/swiotlb.c=2015=static int rmem_swiotlb_device_init(struct reserved_mem *rmem,\n--\nkernel/dma/swiotlb.c-2028-\nkernel/dma/swiotlb.c:2029:\tif (cc_platform_has(CC_ATTR_MEM_ENCRYPT) \u0026\u0026\nkernel/dma/swiotlb.c-2030-\t    !cc_shared_range_valid(rmem-\u003ebase, rmem-\u003esize)) {\n--\nkernel/dma/swiotlb.c-2065-\t\t */\nkernel/dma/swiotlb.c:2066:\t\tif (cc_platform_has(CC_ATTR_MEM_ENCRYPT)) {\nkernel/dma/swiotlb.c-2067-\t\t\tint ret;\n--\nmm/cc_shared.c=166=int alloc_cc_shared_pages_node(int nid, gfp_t gfp,\n--\nmm/cc_shared.c-175-\nmm/cc_shared.c:176:\tif (cc_platform_has(CC_ATTR_MEM_ENCRYPT))\nmm/cc_shared.c-177-\t\treturn __alloc_cc_shared_pages_node(nid, gfp, requested, mem);\n--\nmm/cc_shared.c=213=void free_cc_shared_pages(struct cc_shared_pages *mem)\n--\nmm/cc_shared.c-217-\nmm/cc_shared.c:218:\tif (!cc_platform_has(CC_ATTR_MEM_ENCRYPT))\nmm/cc_shared.c-219-\t\tgoto free_pages;\n"
}

11/2 2026/09/26 15:29 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 44492
							output: 43
							thoughts: 2749
						

Thoughts:
**Analyzing Confidential Computing Patch Series for Reachability**

Okay, let's break this down. My initial focus was on `alloc_cc_shared_pages_node` and `free_cc_shared_pages`. The code looks straightforward, and it *seems* like even without memory encryption, these functions still operate. However, their primary purpose suggests a confidential computing context. The key question is, where are these functions actually *used*?

My deep dive into the call sites led me to `drivers/dma-buf/heaps/system_heap.c`. Specifically, `system_heap_alloc_order` calls `alloc_cc_shared_pages` under a `cc_shared == true` condition, and `system_heap_dma_buf_release` calls `free_cc_shared_pages`. I traced back how `cc_shared` is set – it's tied to the creation of a "system_cc_shared" DMA heap which, I discovered, is *only* enabled when `CC_ATTR_MEM_ENCRYPT` is present.

Now, my radar goes up. The real question is whether this code is reachable in a typical syzkaller environment, which usually runs standard QEMU without confidential computing extensions like AMD SEV/SNP or Intel TDX. Section 1 and 2 of the project's documentation highlights the reachability principle: Fuzzing targets *must* be executable in a standard virtualized setup. If not, they're off-limits.

So, I had to examine the entire patch series to ensure I understood the implications. Let's list those files and summarize what I found:

1.  `arch/arm/mm/dma-mapping.c`: ARM32 only - irrelevant.
2.  `arch/arm64/mm/pageattr.c`: ARM64 only - also irrelevant.
3.  `arch/powerpc/platforms/pseries/svm.c`: PowerPC only - irrelevant.
4.  `arch/s390/mm/init.c`: s390 only - irrelevant.
5.  `arch/x86/coco/tdx/tdx.c`: TDX Guest only - not reachable
    *   `tdx_enc_status_change_prepare` uses `memset` to zero-out a buffer *only* in a TDX guest.
6.  `arch/x86/hyperv/hv_init.c`: Hyper-V SNP guest only - not reachable
    *   `set_memory_decrypted` is called for Hyper-V SNP guests.
7.  `arch/x86/hyperv/ivm.c`: Hyper-V isolated VM (vTOM) only - not reachable
    *   `hv_vtom_clear_present` uses `memset` *only* for Hyper-V isolated VMs.
8.  `arch/x86/kernel/kvmclock.c`: Run once at boot - potentially reachable
    *   This calls `set_memory_decrypted` if `CC_ATTR_GUEST_MEM_ENCRYPT` is set. If not, the memory is cleared. KVM is used by syzkaller so this may be a problem.
9.  `arch/x86/mm/mem_encrypt_amd.c`: SEV/SNP only - not reachable
    *   `amd_enc_status_change_prepare` is SEV/SNP-specific.
10. `drivers/dma-buf/heaps/system_heap.c`: Potentially reachable! This is what I was originally looking at.

Crucially, while most changes are clearly limited to confidential computing environments (specifically AMD SEV/SNP, Intel TDX, and Hyper-V's isolated VMs), there might be a change to the normal `system` heap that I should check.

Finally, I took a closer look at the actual code change in `system_heap_allocate`:

```c
-	sg_set_page(sg, page, page_size(page), 0);
+	unsigned long sg_len;
+
+	sg_len = min_t(unsigned long, page_size(page), sg_remaining);
+	sg_set_page(sg, page, sg_len, 0);
+	sg_remaining -= sg_len;
```

This *is* concerning. This change applies regardless of the `cc_shared` flag. Before the change, it would set the full size of `page`. Now it sets only the remaining size for the page, which seems to prevent an integer overflow in certain cases. *This* looks like a bug fix, and it


12/2 2026/09/26 15:29 tool read-file
0m Args:
{
  "File": "drivers/dma-buf/heaps/system_heap.c",
  "FirstLine": 440,
  "LineCount": 60
}

Results:
{
  "Contents": " 440:\t\t\t\tret = -EINTR;\n 441:\t\t\t\tgoto free_buffer;\n 442:\t\t\t}\n 443:\t\n 444:\t\t\tpage = alloc_largest_available(size_remaining, max_order,\n 445:\t\t\t\t\t\t       cc_shared);\n 446:\t\t\tif (!page)\n 447:\t\t\t\tgoto free_buffer;\n 448:\t\n 449:\t\t\tlist_add_tail(\u0026page-\u003elru, \u0026pages);\n 450:\t\t\tsize_remaining -= page_size(page);\n 451:\t\t\tmax_order = compound_order(page);\n 452:\t\t\ti++;\n 453:\t\t}\n 454:\t\n 455:\t\ttable = \u0026buffer-\u003esg_table;\n 456:\t\tif (sg_alloc_table(table, i, GFP_KERNEL))\n 457:\t\t\tgoto free_buffer;\n 458:\t\n 459:\t\tsg = table-\u003esgl;\n 460:\t\tlist_for_each_entry_safe(page, tmp_page, \u0026pages, lru) {\n 461:\t\t\tunsigned long sg_len;\n 462:\t\n 463:\t\t\tsg_len = min_t(unsigned long, page_size(page), sg_remaining);\n 464:\t\t\tsg_set_page(sg, page, sg_len, 0);\n 465:\t\t\tsg_remaining -= sg_len;\n 466:\t\t\tsg = sg_next(sg);\n 467:\t\t\tlist_del(\u0026page-\u003elru);\n 468:\t\t}\n 469:\t\n 470:\t\t/* create the dmabuf */\n 471:\t\texp_info.exp_name = dma_heap_get_name(heap);\n 472:\t\texp_info.ops = \u0026system_heap_buf_ops;\n 473:\t\texp_info.size = buffer-\u003elen;\n 474:\t\texp_info.flags = fd_flags;\n 475:\t\texp_info.priv = buffer;\n 476:\t\tdmabuf = dma_buf_export(\u0026exp_info);\n 477:\t\tif (IS_ERR(dmabuf)) {\n 478:\t\t\tret = PTR_ERR(dmabuf);\n 479:\t\t\tgoto free_pages;\n 480:\t\t}\n 481:\t\treturn dmabuf;\n 482:\t\n 483:\tfree_pages:\n 484:\t\tfor_each_sgtable_sg(table, sg, i)\n 485:\t\t\tsystem_heap_free_page(sg_page(sg), cc_shared);\n 486:\t\tsg_free_table(table);\n 487:\tfree_buffer:\n 488:\t\tlist_for_each_entry_safe(page, tmp_page, \u0026pages, lru)\n 489:\t\t\tsystem_heap_free_page(page, cc_shared);\n 490:\t\tkfree(buffer);\n 491:\t\n 492:\t\treturn ERR_PTR(ret);\n 493:\t}\n 494:\t\n 495:\tstatic const struct dma_heap_ops system_heap_ops = {\n 496:\t\t.allocate = system_heap_allocate,\n 497:\t};\n 498:\t\n 499:\tstatic struct system_heap_priv system_heap_priv = {\n"
}

13/2 2026/09/26 15:29 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 49205
							output: 30
							thoughts: 1521
						

Thoughts:
**Analyzing DMA Allocation with CMA**

Okay, let's break this down. My initial hunch was correct: I need to focus on how DMA allocations are handled, specifically looking at changes related to the Contiguous Memory Allocator (CMA). It started with a dive into `kernel/dma/direct.c`, `kernel/dma/contiguous.c`, and `kernel/dma/swiotlb.c` to see how things have evolved. The key change is in `dma_direct_alloc`. I immediately spotted that the `attrs` variable is now being passed down the call chain to `__dma_direct_alloc_pages`.

Digging deeper, `__dma_direct_alloc_pages` calls `dma_alloc_contiguous`. And here, the `attrs` value is used for `__DMA_ATTR_ALLOC_CC_SHARED`. The interesting part is what happens within `dma_alloc_contiguous`. It seems like the alignment requirement is calculated:
`required_align = get_order(cc_shared_granule_size());`
`align = min(max(align, required_align), CONFIG_CMA_ALIGNMENT);`

Then it calls `cma_alloc_aligned`. Ah, there it is! The old `cma_alloc_aligned` was simplified to this new version: the alignment is now explicitly passed as an argument. The alignment calculation looks correct, but I want to make sure the CONFIG settings are correct.

Now I am thinking: Is CMA actually enabled? Is this code path even relevant for our use case or the default kernel and fuzzing configurations? I should check `CONFIG_CMA` and `CONFIG_DMA_CMA` to confirm that this CMA code is indeed part of the build. Time to grep the build configs!



14/2 2026/09/26 15:29 tool grepper
0m Args:
{
  "Expression": "config DMA_CMA",
  "PathPrefix": "kernel/dma/"
}

Results:
{
  "Output": "kernel/dma/Kconfig=173=config ARCH_HAS_DMA_ALLOC\n--\nkernel/dma/Kconfig-178-\nkernel/dma/Kconfig:179:config DMA_CMA\nkernel/dma/Kconfig-180-\tbool \"DMA Contiguous Memory Allocator\"\n"
}

15/2 2026/09/26 15:29 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 51230
							output: 38
							thoughts: 3207
						

Thoughts:
**Analyzing Confidential Computing Patches: A Deep Dive into Memory Encryption and Reachability**

Okay, so I've been digging into this patch series, and it's all about beefing up memory security, particularly for confidential computing scenarios. The key seems to be the addition of `memset` calls to functions like `set_memory_decrypted` across various architectures. My initial thought was, "Why are they adding this memset?" But it's becoming clear: it's a critical fix to ensure that when memory is decrypted or shared, potentially sensitive data isn't left lingering. The patch series centralizes this zeroing behavior, removing redundant `memset` calls from callers and introducing `mm/cc_shared.c` for Confidential Computing shared page allocations.

I immediately wanted to assess what is actually reachable in a standard environment, specifically on amd64. Because if this patch modifies unreachable code, it's not worth fuzzing. I meticulously checked the reachability on amd64 in QEMU. I need to make sure I am focusing my time on the important parts.

I started by examining `kernel/dma/direct.c`. It's used by almost all devices, particularly where no IOMMU translation or direct mapping is used (standard QEMU VM). I confirmed that the `memset` call is reached when `!mark_mem_decrypt`. And `mark_mem_decrypt` is set via the `attrs` passed to the `__dma_direct_alloc_pages` calls.

Then I checked `kernel/dma/contiguous.c`, but it is not directly reachable without `attrs`.

I looked at `/dev/dma_heap/system`, which, thankfully, is exposed via Syzkaller and is reachable. It is callable via `ioctl(fd, DMA_HEAP_IOCTL_ALLOC, ...)` by userspace, and it calls `system_heap_allocate`. This is a crucial area.

In `/dev/dma_heap/system`, I found the previously simple `sg_set_page()` being modified to call `sg_len = min_t(unsigned long, page_size(page), sg_remaining);` This is called in *every* allocation, and on free. This is super important!

Finally, I started thinking about `mm/cc_shared.c`. I need to figure out which code paths here are *actually* relevant for QEMU. It appears that `cc_platform_has(CC_ATTR_MEM_ENCRYPT)` would return false in standard QEMU VM, because memory encryption is not active. So it is not *directly* relevant in a standard environment.

So it all depends if there is some software simulation of CC in QEMU that can cause `cc_platform_has(...)` to be true. Let's look at `arch/x86/coco/core.c`!



16/2 2026/09/26 15:29 tool grepper
0m Args:
{
  "Expression": "amd_cc_platform_has",
  "PathPrefix": "arch/x86/coco/core.c"
}

Results:
{
  "Output": "arch/x86/coco/core.c=47=static __maybe_unused __always_inline bool amd_cc_platform_vtom(enum cc_attr attr)\n--\narch/x86/coco/core.c-69- */\narch/x86/coco/core.c:70:static bool noinstr amd_cc_platform_has(enum cc_attr attr)\narch/x86/coco/core.c-71-{\n--\narch/x86/coco/core.c=118=bool noinstr cc_platform_has(enum cc_attr attr)\n--\narch/x86/coco/core.c-121-\tcase CC_VENDOR_AMD:\narch/x86/coco/core.c:122:\t\treturn amd_cc_platform_has(attr);\narch/x86/coco/core.c-123-\tcase CC_VENDOR_INTEL:\n"
}

17/2 2026/09/26 15:29 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 55327
							output: 38
							thoughts: 27
						

Thoughts:
Okay, let's dive into this. Here's my take on those lines of code:

**Analyzing arch/x86/coco/core.c (Lines 70-140)**

Alright, so I'm focusing on lines 70 through 140 of `arch/x86/coco/core.c`. Given that this is in the CoCo (Confidential Computing) directory, and particularly the `core.c` file, I'm expecting to see fundamental functionalities likely related to secure enclave management or interaction.

*   **Initial Setup/Structure:** I'm scanning for function declarations, data structures, and likely some global variables relevant to the core CoCo functionality. I anticipate seeing things like enclave initialization routines, potentially related to memory allocation, page table manipulation (very common in CoCo environments), and security context setup. I’m looking for any initialization of key CoCo data structures like enclave state.
*   **Key Operations:** Lines 70-140 are likely to encompass core operations. I'd expect to see functions dealing with tasks like entering or exiting the secure enclave. This would potentially involve switching control flow, saving and restoring processor state (registers), and handling transitions between the untrusted and trusted execution environments.
*   **Security Mechanisms:** I'm keeping a keen eye out for any specific security checks or integrity verification routines. This could be verification of code integrity, key management, or handling exceptions or interrupts originating from within the enclave. I expect there might be references to memory regions within the enclave and the validation or protection of these memory regions. I'll need to keep an eye out for any code that addresses isolation between the trusted and untrusted worlds.
*   **Potential Challenges:** Depending on the specifics, I'm already anticipating some of the usual challenges. The potential for side-channel attacks, the need for robust exception handling, and memory access control will be critical.

I'm ready to carefully review each line, noting any interesting calls, complex operations, or clever techniques used to maintain confidentiality and integrity. I'll be looking for design patterns that are common in CoCo implementations and making notes. Let's see what's actually in this section.



18/2 2026/09/26 15:29 tool read-file
0m Args:
{
  "File": "arch/x86/coco/core.c",
  "FirstLine": 70,
  "LineCount": 70
}

Results:
{
  "Contents": "  70:\tstatic bool noinstr amd_cc_platform_has(enum cc_attr attr)\n  71:\t{\n  72:\t#ifdef CONFIG_AMD_MEM_ENCRYPT\n  73:\t\n  74:\t\tif (sev_status \u0026 MSR_AMD64_SNP_VTOM)\n  75:\t\t\treturn amd_cc_platform_vtom(attr);\n  76:\t\n  77:\t\tswitch (attr) {\n  78:\t\tcase CC_ATTR_MEM_ENCRYPT:\n  79:\t\t\treturn sme_me_mask;\n  80:\t\n  81:\t\tcase CC_ATTR_HOST_MEM_ENCRYPT:\n  82:\t\t\treturn sme_me_mask \u0026\u0026 !(sev_status \u0026 MSR_AMD64_SEV_ENABLED);\n  83:\t\n  84:\t\tcase CC_ATTR_GUEST_MEM_ENCRYPT:\n  85:\t\t\treturn sev_status \u0026 MSR_AMD64_SEV_ENABLED;\n  86:\t\n  87:\t\tcase CC_ATTR_GUEST_STATE_ENCRYPT:\n  88:\t\t\treturn sev_status \u0026 MSR_AMD64_SEV_ES_ENABLED;\n  89:\t\n  90:\t\t/*\n  91:\t\t * With SEV, the rep string I/O instructions need to be unrolled\n  92:\t\t * but SEV-ES supports them through the #VC handler.\n  93:\t\t */\n  94:\t\tcase CC_ATTR_GUEST_UNROLL_STRING_IO:\n  95:\t\t\treturn (sev_status \u0026 MSR_AMD64_SEV_ENABLED) \u0026\u0026\n  96:\t\t\t\t!(sev_status \u0026 MSR_AMD64_SEV_ES_ENABLED);\n  97:\t\n  98:\t\tcase CC_ATTR_GUEST_SEV_SNP:\n  99:\t\t\treturn sev_status \u0026 MSR_AMD64_SEV_SNP_ENABLED;\n 100:\t\n 101:\t\tcase CC_ATTR_GUEST_SNP_SECURE_TSC:\n 102:\t\t\treturn sev_status \u0026 MSR_AMD64_SNP_SECURE_TSC;\n 103:\t\n 104:\t\tcase CC_ATTR_HOST_SEV_SNP:\n 105:\t\t\treturn cc_flags.host_sev_snp;\n 106:\t\n 107:\t\tcase CC_ATTR_SNP_SECURE_AVIC:\n 108:\t\t\treturn sev_status \u0026 MSR_AMD64_SNP_SECURE_AVIC;\n 109:\t\n 110:\t\tdefault:\n 111:\t\t\treturn false;\n 112:\t\t}\n 113:\t#else\n 114:\t\treturn false;\n 115:\t#endif\n 116:\t}\n 117:\t\n 118:\tbool noinstr cc_platform_has(enum cc_attr attr)\n 119:\t{\n 120:\t\tswitch (cc_vendor) {\n 121:\t\tcase CC_VENDOR_AMD:\n 122:\t\t\treturn amd_cc_platform_has(attr);\n 123:\t\tcase CC_VENDOR_INTEL:\n 124:\t\t\treturn intel_cc_platform_has(attr);\n 125:\t\tdefault:\n 126:\t\t\treturn false;\n 127:\t\t}\n 128:\t}\n 129:\tEXPORT_SYMBOL_GPL(cc_platform_has);\n 130:\t\n 131:\tu64 cc_mkenc(u64 val)\n 132:\t{\n 133:\t\t/*\n 134:\t\t * Both AMD and Intel use a bit in the page table to indicate\n 135:\t\t * encryption status of the page.\n 136:\t\t *\n 137:\t\t * - for AMD, bit *set* means the page is encrypted\n 138:\t\t * - for AMD with vTOM and for Intel, *clear* means encrypted\n 139:\t\t */\n"
}

19/2 2026/09/26 15:29 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 56880
							output: 34
							thoughts: 2674
						

Thoughts:
**Deep Dive into Contiguous Memory Allocation and DMA Operations**

Okay, let's break this down. My initial focus was on understanding how this patch affects contiguous memory allocation, particularly within the context of DMA operations. I started by looking at `drivers/iommu/dma-iommu.c` and specifically `iommu_dma_alloc_pages`, but quickly realized that wasn't the right path. The GIC v3 reference was a dead end because it's ARM-specific. That led me to `kernel/dma/contiguous.c`, the core of the matter.

I zeroed in on `dma_alloc_from_contiguous` and `dma_alloc_contiguous`. The critical change here is how alignment is handled, especially when `__DMA_ATTR_ALLOC_CC_SHARED` is set, which affects "cache-coherent shared" DMA. Previously, alignment in `cma_alloc_aligned` was simply based on the size, but now it incorporates `required_align` from `cc_shared_granule_size()` using `min()` and `max()`, which ensures it meets the defined alignment criteria. It also means that a driver calling `dma_alloc_contiguous()` is influenced when CMA is enabled.

The key realization is that *any* driver allocating contiguous DMA memory when CMA is configured will be affected. This includes essential components like sound cards, video devices (V4L2, vivid), framebuffers, and DRM drivers. This is a much broader impact than I initially anticipated.

I then followed a lead to `system_heap.c`, which exposes a DMA heap through `/dev/dma_heap/system`. It became clear that this could be accessible from userspace. The critical question was whether syzkaller could reach this path. After consulting the documentation, I confirmed that syzkaller definitely tests dma-buf heaps using sys/linux!

The `DMA_HEAP_IOCTL_ALLOC` syscall is the trigger. It calls `system_heap_allocate()` which calls `system_heap_allocate()`. Inside `system_heap_allocate`, the code iterates through pages and sets up scatter-gather lists. I saw that `sg_len` is being initialized to the allocation size and that `sg_remaining` is decremented.

Then, there's `system_heap_buf_ops.release`, and I noticed a new function `system_heap_free_page()`. This means there's a corresponding free operation.

I then expanded my search to look at `kernel/dma/pool.c`. I found that `atomic_pool_expand` also uses `dma_alloc_from_contiguous()` with `attrs`. The key difference here is the use of `min_order` instead of hardcoding `0` in a conditional.

Finally, I checked `kernel/dma/swiotlb.c` to see how it's affected. It appears that `swiotlb` also aligns to `cc_shared_granule_size()` when using `__DMA_ATTR_ALLOC_CC_SHARED`. Both `alloc_dma_pages()` and `swiotlb_free_tlb()` are affected by this alignment. This is critical for boot time because of `swiotlb_init_remap` and `swiotlb_init_late`.

I also briefly looked at `kernel/kexec_file.c` which calls `dma_alloc_from_contiguous` during the `kexec_file_load` system call, but quickly moved on.

In conclusion, this patch introduces important alignment changes across the board. The key implications are in the way CMA is used and how memory is allocated for "cache-coherent shared" DMA which has a substantial impact. This affects a wide range of devices and drivers, including those accessible through the DMA heap syscall from userspace, which is tested by syzkaller.



20/2 2026/09/26 15:29 tool grepper
0m Args:
{
  "Expression": "config DMABUF_HEAPS",
  "PathPrefix": "drivers/dma-buf/"
}

Results:
{
  "Output": "drivers/dma-buf/Kconfig=52=config DMABUF_KUNIT_TEST\n--\ndrivers/dma-buf/Kconfig-59-\ndrivers/dma-buf/Kconfig:60:menuconfig DMABUF_HEAPS\ndrivers/dma-buf/Kconfig-61-\tbool \"DMA-BUF Userland Memory Heaps\"\n--\ndrivers/dma-buf/heaps/Kconfig:1:config DMABUF_HEAPS_SYSTEM\ndrivers/dma-buf/heaps/Kconfig-2-\ttristate \"DMA-BUF System Heap\"\n--\ndrivers/dma-buf/heaps/Kconfig-7-\ndrivers/dma-buf/heaps/Kconfig:8:config DMABUF_HEAPS_SYSTEM_CC_SHARED\ndrivers/dma-buf/heaps/Kconfig-9-\tbool \"DMA-BUF System Heap for decrypted CoCo VMs\"\n--\ndrivers/dma-buf/heaps/Kconfig-15-\ndrivers/dma-buf/heaps/Kconfig:16:config DMABUF_HEAPS_CMA\ndrivers/dma-buf/heaps/Kconfig-17-\ttristate \"DMA-BUF CMA Heap\"\n"
}

21/2 2026/09/26 15:29 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 60726
							output: 38
							thoughts: 71
						

Thoughts:
Here's my summary, as the expert:

**Investigating DMA-BUF Heap Configuration Options**

Alright, let's dive into the Kconfig for DMA-BUF heaps. I'm looking at `drivers/dma-buf/heaps/Kconfig` to get a handle on the available configuration options. I see two main configurations of interest near the top: `DMABUF_HEAPS_SYSTEM` on line 1 and `DMABUF_HEAPS_SYSTEM_CC_SHARED` on line 8. My initial thoughts are that `DMABUF_HEAPS_SYSTEM` probably defines the base support for system-level DMA-BUF heaps, and I'll need to understand it better. The presence of `DMABUF_HEAPS_SYSTEM_CC_SHARED` suggests some kind of cross-compiler (CC) or cross-domain shared memory configuration on top of that. I need to figure out how these two relate. Let's delve deeper into that Kconfig file; I need to understand the dependencies, default values, and implications of enabling these options. It's a key piece of the puzzle for this DMA-BUF heap architecture.



22/2 2026/09/26 15:29 tool read-file
0m Args:
{
  "File": "drivers/dma-buf/heaps/Kconfig",
  "FirstLine": 1,
  "LineCount": 30
}

Results:
{
  "Contents": "   1:\tconfig DMABUF_HEAPS_SYSTEM\n   2:\t\ttristate \"DMA-BUF System Heap\"\n   3:\t\tdepends on DMABUF_HEAPS\n   4:\t\thelp\n   5:\t\t  Choose this option to enable the system dmabuf heap. The system heap\n   6:\t\t  is backed by pages from the buddy allocator. If in doubt, say Y.\n   7:\t\n   8:\tconfig DMABUF_HEAPS_SYSTEM_CC_SHARED\n   9:\t\tbool \"DMA-BUF System Heap for decrypted CoCo VMs\"\n  10:\t\tdepends on DMABUF_HEAPS \u0026\u0026 ARCH_HAS_MEM_ENCRYPT \u0026\u0026 DMABUF_HEAPS_SYSTEM=y\n  11:\t\thelp\n  12:\t\t  Choose this option to enable the system_cc_shared dmabuf heap. This\n  13:\t\t  allows allocating shared (decrypted) memory for confidential computing\n  14:\t\t  (CoCo) VMs.\n  15:\t\n  16:\tconfig DMABUF_HEAPS_CMA\n  17:\t\ttristate \"DMA-BUF CMA Heap\"\n  18:\t\tdepends on DMABUF_HEAPS \u0026\u0026 DMA_CMA\n  19:\t\thelp\n  20:\t\t  Choose this option to enable dma-buf CMA heap. This heap is backed\n  21:\t\t  by the Contiguous Memory Allocator (CMA). If your system has these\n  22:\t\t  regions, you should say Y here.\n"
}

23/2 2026/09/26 15:29 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 61447
							output: 21
							thoughts: 6321
						

Thoughts:
**Patch Fuzzing Analysis: WorthFuzzing = True**

Okay, so I've been digging into this patch series and it's looking like this is definitely something we need to fuzz. At first, I was a little concerned that this patch series was entirely focused on confidential computing features like memory encryption, which might not be enabled in standard QEMU environments. But, after careful analysis, I've concluded that this is a *MUST FUZZ* scenario. Here's the breakdown:

The patch introduces confidential computing support, modifying several core DMA and memory management components. Even without the specific hardware features enabled, the patch touches reachable code paths. My analysis hinges on the fact that DMA operations and the `/dev/dma_heap/system` device are part of the standard Linux kernel and are exercised in typical systems. Crucially, the patch changes allocation and release paths in `system_heap.c`, CMA allocation paths in `kernel/dma/contiguous.c`, core DMA mapping paths in `kernel/dma/direct.c`, DMA atomic pool in `kernel/dma/pool.c`, contiguous allocation used during `kexec_file_load` in `kernel/kexec_file.c`, and CMA-based allocation via the direct allocator.

Specifically, the patch modifies:

*   The allocation logic in `system_heap_allocate`, changing how memory pages are set in scatter-gather lists. It also touches `system_heap_dma_buf_release`, which calls `system_heap_free_page`. These functions are part of the standard dma-buf system heap mechanism, and they are reachable, which is why I've come to the conclusion that this is a worthwhile fuzzing target.
*   CMA DMA allocation functions (`dma_alloc_from_contiguous`, `dma_alloc_contiguous`). These are core DMA functions, and even if CMA is only using the default, the changes affect the underlying allocation logic.
*   The direct DMA allocator (`dma_direct_alloc`, `dma_direct_free`), which also now has new alignment constraints.
*   The DMA atomic pool's expansion logic (`atomic_pool_expand`).
*   The allocation used in kexec (`kexec_alloc_contig`).

The modified code *is* reachable, and the patch makes refactorings and introduces potentially new assertions, so fuzzing is critical.

**Focus Symbols:**

Based on that, I'll recommend the following `FocusSymbols`:

*   `system_heap_allocate`: A direct function pointer in the system_heap_ops struct, and a key entry point.
*   `system_heap_dma_buf_release`: A function pointer in the system_heap_buf_ops, a core dma-buf release function.
*   `dma_alloc_from_contiguous`: DMA helper.
*   `dma_alloc_contiguous`: DMA helper.
*   `kexec_alloc_contig`: Contiguous allocation used during kexec load.

I'll also keep a close eye on `cc_shared_calc_layout`, `cc_make_shared`, and `cc_make_private` but they will most likely need to be included in `EnableConfigs` to be enabled.

In short, this is not just a straightforward refactoring or CoCo-specific change. It introduces new logic and modifies core DMA operations in a way that *must* be tested to ensure there are no regressions or latent bugs.



24/2 2026/09/26 15:30 tool grepper
0m Args:
{
  "Expression": "force_dma_unencrypted"
}

Results:
{
  "Output": "Documentation/arch/x86/tdx.rst=489=For coherent DMA allocation, the DMA buffer gets converted on the\nDocumentation/arch/x86/tdx.rst:490:allocation. Check force_dma_unencrypted() for details.\nDocumentation/arch/x86/tdx.rst-491-\n--\narch/arm64/include/asm/mem_encrypt.h=20=int realm_register_memory_enc_ops(void);\narch/arm64/include/asm/mem_encrypt.h-21-\narch/arm64/include/asm/mem_encrypt.h:22:static inline bool force_dma_unencrypted(struct device *dev)\narch/arm64/include/asm/mem_encrypt.h-23-{\n--\narch/powerpc/include/asm/mem_encrypt.h=14=struct device;\narch/powerpc/include/asm/mem_encrypt.h-15-\narch/powerpc/include/asm/mem_encrypt.h:16:static inline bool force_dma_unencrypted(struct device *dev)\narch/powerpc/include/asm/mem_encrypt.h-17-{\n--\narch/s390/mm/init.c=129=int set_memory_decrypted(unsigned long vaddr, int numpages)\n--\narch/s390/mm/init.c-143-/* are we a protected virtualization guest? */\narch/s390/mm/init.c:144:bool force_dma_unencrypted(struct device *dev)\narch/s390/mm/init.c-145-{\n--\narch/x86/mm/mem_encrypt.c-19-/* Override for DMA direct allocation check - ARCH_HAS_FORCE_DMA_UNENCRYPTED */\narch/x86/mm/mem_encrypt.c:20:bool force_dma_unencrypted(struct device *dev)\narch/x86/mm/mem_encrypt.c-21-{\n--\ninclude/linux/dma-direct.h=114=static inline phys_addr_t dma_to_phys(struct device *dev, dma_addr_t dma_addr)\n--\ninclude/linux/dma-direct.h-128-#ifdef CONFIG_ARCH_HAS_FORCE_DMA_UNENCRYPTED\ninclude/linux/dma-direct.h:129:bool force_dma_unencrypted(struct device *dev);\ninclude/linux/dma-direct.h-130-#else\ninclude/linux/dma-direct.h:131:static inline bool force_dma_unencrypted(struct device *dev)\ninclude/linux/dma-direct.h-132-{\n--\ninclude/linux/dma-direct.h=137=static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size,\n--\ninclude/linux/dma-direct.h-148-\t */\ninclude/linux/dma-direct.h:149:\tif (!(attrs \u0026 DMA_ATTR_CC_SHARED) \u0026\u0026 force_dma_unencrypted(dev))\ninclude/linux/dma-direct.h-150-\t\treturn false;\n--\nkernel/dma/direct.c=42=u64 dma_direct_get_required_mask(struct device *dev)\nkernel/dma/direct.c-43-{\nkernel/dma/direct.c:44:\tbool require_decrypted = force_dma_unencrypted(dev);\nkernel/dma/direct.c-45-\tphys_addr_t phys = ((phys_addr_t)max_pfn \u003c\u003c PAGE_SHIFT) - 1;\n--\nkernel/dma/direct.c=73=bool dma_coherent_ok(struct device *dev, phys_addr_t phys, size_t size)\nkernel/dma/direct.c-74-{\nkernel/dma/direct.c:75:\tbool require_decrypted = force_dma_unencrypted(dev);\nkernel/dma/direct.c-76-\tdma_addr_t dma_addr = phys_to_dma_direct(dev, phys, require_decrypted);\n--\nkernel/dma/direct.c=202=void *dma_direct_alloc(struct device *dev, size_t size,\n--\nkernel/dma/direct.c-211-\nkernel/dma/direct.c:212:\tif (force_dma_unencrypted(dev))\nkernel/dma/direct.c-213-\t\tattrs |= __DMA_ATTR_ALLOC_CC_SHARED;\n--\nkernel/dma/direct.c=369=void dma_direct_free(struct device *dev, size_t size,\n--\nkernel/dma/direct.c-381-\t */\nkernel/dma/direct.c:382:\tif (force_dma_unencrypted(dev))\nkernel/dma/direct.c-383-\t\tattrs |= __DMA_ATTR_ALLOC_CC_SHARED;\n--\nkernel/dma/direct.c=447=struct page *dma_direct_alloc_pages(struct device *dev, size_t size,\n--\nkernel/dma/direct.c-455-\nkernel/dma/direct.c:456:\tif (force_dma_unencrypted(dev))\nkernel/dma/direct.c-457-\t\tattrs |= __DMA_ATTR_ALLOC_CC_SHARED;\n--\nkernel/dma/direct.c=500=void dma_direct_free_pages(struct device *dev, size_t size,\n--\nkernel/dma/direct.c-511-\t */\nkernel/dma/direct.c:512:\tbool mark_mem_encrypted = force_dma_unencrypted(dev);\nkernel/dma/direct.c-513-\n--\nkernel/dma/direct.c=676=int dma_direct_mmap(struct device *dev, struct vm_area_struct *vma,\n--\nkernel/dma/direct.c-686-\nkernel/dma/direct.c:687:\tif (force_dma_unencrypted(dev))\nkernel/dma/direct.c-688-\t\tattrs |= DMA_ATTR_CC_SHARED;\n--\nkernel/dma/direct.c=831=size_t dma_direct_max_mapping_size(struct device *dev)\n--\nkernel/dma/direct.c-835-\t    (dma_addressing_limited(dev) || is_swiotlb_force_bounce(dev) ||\nkernel/dma/direct.c:836:\t     force_dma_unencrypted(dev)))\nkernel/dma/direct.c-837-\t\treturn swiotlb_max_mapping_size(dev);\n--\nkernel/dma/mapping.c=633=void *dma_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle,\n--\nkernel/dma/mapping.c-654-\nkernel/dma/mapping.c:655:\tif (force_dma_unencrypted(dev))\nkernel/dma/mapping.c-656-\t\tattrs |= __DMA_ATTR_ALLOC_CC_SHARED;\n--\nkernel/dma/swiotlb.c=1554=phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr,\n--\nkernel/dma/swiotlb.c-1578-\t\t/* swiotlb pool is incorrect for this device */\nkernel/dma/swiotlb.c:1579:\t\tif (unlikely(mem-\u003ecc_shared != force_dma_unencrypted(dev)))\nkernel/dma/swiotlb.c-1580-\t\t\treturn (phys_addr_t)DMA_MAPPING_ERROR;\n"
}

25/2 2026/09/26 15:30 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 70517
							output: 40
							thoughts: 30
						

Thoughts:
Okay, let's dive into this. Here's my take on that function, `arch/x86/mm/mem_encrypt.c:force_dma_unencrypted`.

**Analyzing `force_dma_unencrypted` for DMA Security Considerations**

Alright, so I need to understand what this `force_dma_unencrypted` function is all about. Based on the name, my initial hypothesis is that it's related to handling Direct Memory Access (DMA) operations when memory encryption is active, most likely the Intel Total Memory Encryption (TME) or similar technologies. The name suggests this function is involved in *disabling* memory encryption for DMA transfers, or at least ensuring the DMA transfer happens with unencrypted memory.

My expert intuition suggests this is critical for a couple of reasons. DMA controllers, especially older or more specialized ones, might not be designed to interact with encrypted memory directly. Trying to DMA into or out of encrypted regions without proper handling would lead to corrupted data or system crashes. There could be hardware limitations in the DMA controller, or simply the added complexity of implementing encryption/decryption in the DMA engine might be undesirable.

Therefore, this function is probably designed to ensure secure DMA operations by either bypassing encryption or making sure the DMA is directed to non-encrypted memory regions. I'd expect to see it handling issues related to:

*   **Security Context:** Setting up the proper security context to ensure the DMA controller can access memory safely, potentially creating specific mappings.
*   **Memory Allocation:** If this function actually forces the memory to be unencrypted it likely involves allocating or remapping DMA buffers into unencrypted memory regions.
*   **Potential Trade-offs:** There are probably trade-offs involved. Forcing unencrypted DMA might impact overall security posture if it exposes a sensitive data path.
*   **Error handling:** I would definitely expect this function to address error conditions, ensuring that if it cannot guarantee an unencrypted transfer, the function returns an error, preventing potentially insecure behavior.

Now, to understand the specifics, I'll need to dig into the actual code to see *how* it's doing this, the conditions under which it's triggered, and any associated comments. That's where the real insights are. This name implies the existence of a function that either has a direct or indirect impact on security considerations for DMA.



26/2 2026/09/26 15:30 tool read-file
0m Args:
{
  "File": "arch/x86/mm/mem_encrypt.c",
  "FirstLine": 18,
  "LineCount": 20
}

Results:
{
  "Contents": "  18:\t\n  19:\t/* Override for DMA direct allocation check - ARCH_HAS_FORCE_DMA_UNENCRYPTED */\n  20:\tbool force_dma_unencrypted(struct device *dev)\n  21:\t{\n  22:\t\t/*\n  23:\t\t * For SEV, all DMA must be to unencrypted addresses.\n  24:\t\t */\n  25:\t\tif (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT))\n  26:\t\t\treturn true;\n  27:\t\n  28:\t\t/*\n  29:\t\t * For SME, all DMA must be to unencrypted addresses if the\n  30:\t\t * device does not support DMA to addresses that include the\n  31:\t\t * encryption mask.\n  32:\t\t */\n  33:\t\tif (cc_platform_has(CC_ATTR_HOST_MEM_ENCRYPT)) {\n  34:\t\t\tu64 dma_enc_mask = DMA_BIT_MASK(__ffs64(sme_me_mask));\n  35:\t\t\tu64 dma_dev_mask = min_not_zero(dev-\u003ecoherent_dma_mask,\n  36:\t\t\t\t\t\t\tdev-\u003ebus_dma_limit);\n  37:\t\n"
}

27/2 2026/09/26 15:30 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 71404
							output: 38
							thoughts: 3221
						

Thoughts:
**Analyzing DMA Memory Allocation and CC Shared Memory**

Okay, so I'm diving into this DMA and shared memory code, specifically looking at how the kernel allocates and frees memory, especially concerning CC (Confidential Computing) shared memory. My focus is on the `cc_shared` functionality and where it touches the DMA subsystem.

First, that `cc_shared_granule_size` function caught my eye, with its assertion ensuring the granule size is a power of 2 and at least a page size. I'm noting the calls to `cc_shared_calc_layout`, `cc_make_shared`, and related functions. This points directly to the core CC shared memory mechanisms. The `free_cc_shared_pages` function is crucial, and I tracked its call in `system_heap_free_page`, which I found in `drivers/dma-buf/heaps/system_heap.c`. The conditional logic surrounding `cc_shared` is important here. It seems that `free_cc_shared_pages` is called when `cc_shared` is true, indicating the memory management path for CC shared buffers. I also briefly noted the ARM64-specific `irq-gic-v3-its.c` and its use of `free_cc_shared_pages`.

Now, I'm checking the broader DMA allocation functions in `kernel/dma/contiguous.c`. I'm concerned with the functions `dma_alloc_contiguous`, `dma_alloc_from_contiguous`, and `cma_alloc_aligned` and want to verify if they are relevant on amd64. I see these functions are called, and they are important for CMA (Contiguous Memory Allocator).

I went back to `drivers/dma-buf/heaps/system_heap.c`. `system_heap_allocate`, `system_heap_dma_buf_release`, `alloc_largest_available` are important functions, which I see are called via ioctl and release callbacks. I also checked `kernel/dma/direct.c` and its ubiquitous `dma_direct_alloc` and related functions.

Now, to refine the focus for fuzzing, I'm carefully selecting the specific functions implementing altered logic. Key candidates include: `system_heap_allocate`, `system_heap_dma_buf_release`, the new `system_heap_alloc_order` and `system_heap_free_page`, `dma_alloc_contiguous`, `dma_alloc_from_contiguous`, `cma_alloc_aligned`, `atomic_pool_expand`, and `kexec_alloc_contig`. These are where the core changes and interactions occur.

I then dug into `mm/cc_shared.c` and found that `cc_shared_calc_layout` is called by several DMA-related functions, like `dma_alloc_contiguous`, `dma_alloc_from_contiguous`, `atomic_pool_expand`, `system_heap_allocate`. It's only called when `__DMA_ATTR_ALLOC_CC_SHARED` is set, or `dma_pool->cc_shared` or `system_heap`'s `cc_shared` is true. Then I checked how `__DMA_ATTR_ALLOC_CC_SHARED` is set, and it seems to depend on `force_dma_unencrypted(dev)`, `dma_pool->cc_shared`, or `system_heap_cc_shared_priv`. These conditions depend on the platform's CC features. It seems that in a standard amd64 environment (without CC hardware or CC enabled), `cc_platform_has(CC_ATTR_MEM_ENCRYPT)` is false, but some code paths are still triggered.

Finally, I verified that `system_heap_allocate`, `system_heap_dma_buf_release`, `dma_alloc_contiguous` and `dma_alloc_from_contiguous` are non-static symbols, while `system_heap_alloc_order` and `system_heap_free_page` are static. This ensures they are available in vmlinux.



28/2 2026/09/26 15:30 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "EXPORT_SYMBOL.*dma_alloc.*contiguous",
  "PathPrefix": "kernel/dma/contiguous.c"
}

Results:
{
  "error": "no matches"
}

29/2 2026/09/26 15:30 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 75569
							output: 34
							thoughts: 17
						

Thoughts:
Okay, here's my take on those thoughts, tailored for an expert audience:

**Diving into `kernel/dma/contiguous.c` – Let's Get Specific**

Alright, time to roll up the sleeves. I'm going to start by zeroing in on the file `kernel/dma/contiguous.c`. I know this code pretty well, but given the nature of the task at hand, I'm going to quickly check the end of the file. No point starting at the top if the information I need might be right at the end. I anticipate the specifics of what I need are going to be defined at the end of the module.



30/2 2026/09/26 15:30 tool grepper
0m Args:
{
  "Expression": "dma_alloc_contiguous",
  "PathPrefix": "kernel/dma/contiguous.c"
}

Results:
{
  "Output": "kernel/dma/contiguous.c=404=static struct page *cma_alloc_aligned(struct cma *cma, size_t size, gfp_t gfp,\n--\nkernel/dma/contiguous.c-410-/**\nkernel/dma/contiguous.c:411: * dma_alloc_contiguous() - allocate contiguous pages\nkernel/dma/contiguous.c-412- * @dev:   Pointer to device for which the allocation is performed.\n--\nkernel/dma/contiguous.c-425- */\nkernel/dma/contiguous.c:426:struct page *dma_alloc_contiguous(struct device *dev, size_t size,\nkernel/dma/contiguous.c-427-\t\t\t\t  gfp_t gfp, unsigned long attrs)\n--\nkernel/dma/contiguous.c-470- *\nkernel/dma/contiguous.c:471: * This function releases memory allocated by dma_alloc_contiguous(). As the\nkernel/dma/contiguous.c-472- * cma_release returns false when provided pages do not belong to contiguous\n"
}

31/2 2026/09/26 15:30 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 76043
							output: 36
							thoughts: 25
						

Thoughts:
Okay, let's dive into this. Here's my take on those lines of code:

**Analyzing `kernel/dma/contiguous.c` - Lines 430-490**

Alright, I'm zeroing in on lines 430-490 within `kernel/dma/contiguous.c`. Based on the context, I'm expecting to find core DMA contiguous memory management routines here. This is a critical area, so I need to be precise. I'm guessing these lines likely handle allocation or deallocation of physically contiguous memory regions, or possibly the mapping/unmapping functions. Considering the file name, it almost certainly deals with managing physically contiguous memory blocks for DMA operations, which makes them prime targets for potential issues, like fragmentation or alignment problems. If I were a betting person, I'd say the code is probably touching the `dma_contiguous_region` structure or similar data structure. I'll be looking for any signs of lock contention or race conditions. Also, I'll be noting how the code handles error paths or edge cases, such as insufficient physical memory. I'll need to check how the code interacts with the physical memory allocator. How does it handle allocation failures? Does it provide any helpful diagnostic information? Are there any potential performance bottlenecks I should be aware of? I'll also be searching for how the code ensures that the allocated memory region meets the DMA hardware's alignment requirements. Any tricks used to get around potential limitations? Let's get to the code and see.



32/2 2026/09/26 15:30 tool read-file
0m Args:
{
  "File": "kernel/dma/contiguous.c",
  "FirstLine": 460,
  "LineCount": 40
}

Results:
{
  "Contents": " 460:\t\t\treturn NULL;\n 461:\t\n 462:\t\treturn cma_alloc_aligned(dma_contiguous_default_area, size, gfp, align);\n 463:\t}\n 464:\t\n 465:\t/**\n 466:\t * dma_free_contiguous() - release allocated pages\n 467:\t * @dev:   Pointer to device for which the pages were allocated.\n 468:\t * @page:  Pointer to the allocated pages.\n 469:\t * @size:  Size of allocated pages.\n 470:\t *\n 471:\t * This function releases memory allocated by dma_alloc_contiguous(). As the\n 472:\t * cma_release returns false when provided pages do not belong to contiguous\n 473:\t * area and true otherwise, this function then does a fallback __free_pages()\n 474:\t * upon a false-return.\n 475:\t */\n 476:\tvoid dma_free_contiguous(struct device *dev, struct page *page, size_t size)\n 477:\t{\n 478:\t\tunsigned int count = PAGE_ALIGN(size) \u003e\u003e PAGE_SHIFT;\n 479:\t\n 480:\t\t/* if dev has its own cma, free page from there */\n 481:\t\tif (dev-\u003ecma_area) {\n 482:\t\t\tif (cma_release(dev-\u003ecma_area, page, count))\n 483:\t\t\t\treturn;\n 484:\t\t} else {\n 485:\t\t\t/*\n 486:\t\t\t * otherwise, page is from either per-numa cma or default cma\n 487:\t\t\t */\n 488:\t#ifdef CONFIG_DMA_NUMA_CMA\n 489:\t\t\tif (cma_release(dma_contiguous_numa_area[page_to_nid(page)],\n 490:\t\t\t\t\t\tpage, count))\n 491:\t\t\t\treturn;\n 492:\t#endif\n 493:\t\t\tif (cma_release(dma_contiguous_default_area, page, count))\n 494:\t\t\t\treturn;\n 495:\t\t}\n 496:\t\n 497:\t\t/* not in any cma, free from buddy */\n 498:\t\t__free_pages(page, get_order(size));\n 499:\t}\n"
}

33/2 2026/09/26 15:30 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 77052
							output: 28
							thoughts: 84
						

Thoughts:
Here's my thinking process:

**Investigating DMA Contiguous Allocation Configuration Requirements**

Okay, so I'm looking at `dma_alloc_contiguous` and `dma_alloc_from_contiguous`. These are critical functions, non-static, so likely part of the core DMA API, and defined in `include/linux/dma-map-ops.h`. My immediate question is: What kernel configurations actually *enable* these? I need to figure out the dependencies.

My next step is to drill down into how the relevant object files are built. I'll check `kernel/dma/Makefile` to see how `contiguous.o`, `direct.o`, and `pool.o` are built. These are the most likely candidates to contain the implementation or at least the configuration hooks for these allocation functions. The Makefile will show me the conditional compilation flags or dependencies I need to understand. That should give me the configuration clues I need.



34/2 2026/09/26 15:31 tool grepper
0m Args:
{
  "Expression": "contiguous\\.o",
  "PathPrefix": "kernel/dma/"
}

Results:
{
  "Output": "kernel/dma/Makefile=5=obj-$(CONFIG_ARCH_HAS_DMA_OPS)\t\t+= dummy.o\nkernel/dma/Makefile:6:obj-$(CONFIG_DMA_CMA)\t\t\t+= contiguous.o\nkernel/dma/Makefile-7-obj-$(CONFIG_DMA_DECLARE_COHERENT)\t+= coherent.o\n"
}

35/2 2026/09/26 15:31 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 77459
							output: 28
							thoughts: 37
						

Thoughts:
Okay, here's my take on those thoughts, tailored for an expert audience:

**DMA-CMA Module Inclusion and Resource Allocation**

Right, so I see we're explicitly linking `contiguous.o` into the `obj-$(CONFIG_DMA_CMA)` target. Makes sense, assuming we're dealing with the CMA (Contiguous Memory Allocator) configuration option. That's a clean way to handle it, keeps it contained. But, hold on a second... what about `pool.o`? That's also a fundamental part of the CMA implementation, isn't it? Isn't it likely part of the module?

I need to confirm if it’s added in the Makefile. Time to delve into `kernel/dma/Makefile` to make sure we're not missing a crucial piece of the puzzle here. We need to make sure the build system is correctly managing the dependencies and linking all the necessary components for a functional CMA setup. Otherwise, we'll have runtime issues. Let's see if we need to explicitly add `pool.o` or if it's included elsewhere. It’s always best to be explicit about these things.



36/2 2026/09/26 15:31 tool grepper
0m Args:
{
  "Expression": "pool\\.o",
  "PathPrefix": "kernel/dma/"
}

Results:
{
  "Output": "kernel/dma/Makefile=9=obj-$(CONFIG_SWIOTLB)\t\t\t+= swiotlb.o\nkernel/dma/Makefile:10:obj-$(CONFIG_DMA_COHERENT_POOL)\t\t+= pool.o\nkernel/dma/Makefile-11-obj-$(CONFIG_MMU)\t\t\t+= remap.o\n"
}

37/2 2026/09/26 15:31 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 77865
							output: 28
							thoughts: 8
						

Thoughts:
Okay, here's my summary, in the first person, as requested:

**Considering a Specific Object File**

Alright, let's see. I'm deep in this project, and I'm running through the potential implications of various components. My mind keeps circling around this `direct.o` file. Specifically, I'm thinking about the role of `direct.o`. What about it? Does it seem correct in its current form? Are there any potential flaws that need to be addressed? Is it dependent on other parts of the project? What impacts would it have in production?



38/2 2026/09/26 15:31 tool grepper
0m Args:
{
  "Expression": "direct\\.o",
  "PathPrefix": "kernel/dma/"
}

Results:
{
  "Output": "kernel/dma/Makefile-2-\nkernel/dma/Makefile:3:obj-$(CONFIG_HAS_DMA)\t\t\t+= mapping.o direct.o\nkernel/dma/Makefile-4-obj-$(CONFIG_DMA_OPS_HELPERS)\t\t+= ops_helpers.o\n"
}

39/2 2026/09/26 15:31 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 78103
							output: 33
							thoughts: 27
						

Thoughts:
Okay, here's my take on those thoughts, framed as an expert's internal monologue:

**Investigating a Potential Memory Management Issue**

Hmm, so we're seeing something that smells like a memory allocation or management issue, potentially within the kernel. Let's dig in and figure out where this is coming from. The mention of `system_heap.o` immediately makes me think about potential DMA buffer allocation problems. It's a key piece of the puzzle. My first instinct, given that, is to immediately check the `drivers/dma-buf/heaps/Makefile`. That's where the DMA buffer heap implementation is defined. This is the obvious starting point. If there are problems in that area, the makefile is the place to start diagnosing the source.



40/2 2026/09/26 15:31 tool grepper
0m Args:
{
  "Expression": "system_heap",
  "PathPrefix": "drivers/dma-buf/heaps/"
}

Results:
{
  "Output": "drivers/dma-buf/heaps/Makefile-1-# SPDX-License-Identifier: GPL-2.0\ndrivers/dma-buf/heaps/Makefile:2:obj-$(CONFIG_DMABUF_HEAPS_SYSTEM)\t+= system_heap.o\ndrivers/dma-buf/heaps/Makefile-3-obj-$(CONFIG_DMABUF_HEAPS_CMA)\t\t+= cma_heap.o\n--\ndrivers/dma-buf/heaps/system_heap.c-26-\ndrivers/dma-buf/heaps/system_heap.c:27:struct system_heap_priv {\ndrivers/dma-buf/heaps/system_heap.c-28-\tbool cc_shared;\n--\ndrivers/dma-buf/heaps/system_heap.c-30-\ndrivers/dma-buf/heaps/system_heap.c:31:struct system_heap_buffer {\ndrivers/dma-buf/heaps/system_heap.c-32-\tstruct dma_heap *heap;\n--\ndrivers/dma-buf/heaps/system_heap.c=67=static int dup_sg_table(struct sg_table *from, struct sg_table *to)\n--\ndrivers/dma-buf/heaps/system_heap.c-84-\ndrivers/dma-buf/heaps/system_heap.c:85:static int system_heap_attach(struct dma_buf *dmabuf,\ndrivers/dma-buf/heaps/system_heap.c-86-\t\t\t      struct dma_buf_attachment *attachment)\ndrivers/dma-buf/heaps/system_heap.c-87-{\ndrivers/dma-buf/heaps/system_heap.c:88:\tstruct system_heap_buffer *buffer = dmabuf-\u003epriv;\ndrivers/dma-buf/heaps/system_heap.c-89-\tstruct dma_heap_attachment *a;\n--\ndrivers/dma-buf/heaps/system_heap.c-115-\ndrivers/dma-buf/heaps/system_heap.c:116:static void system_heap_detach(struct dma_buf *dmabuf,\ndrivers/dma-buf/heaps/system_heap.c-117-\t\t\t       struct dma_buf_attachment *attachment)\ndrivers/dma-buf/heaps/system_heap.c-118-{\ndrivers/dma-buf/heaps/system_heap.c:119:\tstruct system_heap_buffer *buffer = dmabuf-\u003epriv;\ndrivers/dma-buf/heaps/system_heap.c-120-\tstruct dma_heap_attachment *a = attachment-\u003epriv;\n--\ndrivers/dma-buf/heaps/system_heap.c-129-\ndrivers/dma-buf/heaps/system_heap.c:130:static struct sg_table *system_heap_map_dma_buf(struct dma_buf_attachment *attachment,\ndrivers/dma-buf/heaps/system_heap.c-131-\t\t\t\t\t\tenum dma_data_direction direction)\n--\ndrivers/dma-buf/heaps/system_heap.c-146-\ndrivers/dma-buf/heaps/system_heap.c:147:static void system_heap_unmap_dma_buf(struct dma_buf_attachment *attachment,\ndrivers/dma-buf/heaps/system_heap.c-148-\t\t\t\t      struct sg_table *table,\n--\ndrivers/dma-buf/heaps/system_heap.c-156-\ndrivers/dma-buf/heaps/system_heap.c:157:static int system_heap_dma_buf_begin_cpu_access(struct dma_buf *dmabuf,\ndrivers/dma-buf/heaps/system_heap.c-158-\t\t\t\t\t\tenum dma_data_direction direction)\ndrivers/dma-buf/heaps/system_heap.c-159-{\ndrivers/dma-buf/heaps/system_heap.c:160:\tstruct system_heap_buffer *buffer = dmabuf-\u003epriv;\ndrivers/dma-buf/heaps/system_heap.c-161-\tstruct dma_heap_attachment *a;\n--\ndrivers/dma-buf/heaps/system_heap.c-177-\ndrivers/dma-buf/heaps/system_heap.c:178:static int system_heap_dma_buf_end_cpu_access(struct dma_buf *dmabuf,\ndrivers/dma-buf/heaps/system_heap.c-179-\t\t\t\t\t      enum dma_data_direction direction)\ndrivers/dma-buf/heaps/system_heap.c-180-{\ndrivers/dma-buf/heaps/system_heap.c:181:\tstruct system_heap_buffer *buffer = dmabuf-\u003epriv;\ndrivers/dma-buf/heaps/system_heap.c-182-\tstruct dma_heap_attachment *a;\n--\ndrivers/dma-buf/heaps/system_heap.c-198-\ndrivers/dma-buf/heaps/system_heap.c:199:static int system_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)\ndrivers/dma-buf/heaps/system_heap.c-200-{\ndrivers/dma-buf/heaps/system_heap.c:201:\tstruct system_heap_buffer *buffer = dmabuf-\u003epriv;\ndrivers/dma-buf/heaps/system_heap.c-202-\tstruct sg_table *table = \u0026buffer-\u003esg_table;\n--\ndrivers/dma-buf/heaps/system_heap.c-239-\ndrivers/dma-buf/heaps/system_heap.c:240:static void *system_heap_do_vmap(struct system_heap_buffer *buffer)\ndrivers/dma-buf/heaps/system_heap.c-241-{\n--\ndrivers/dma-buf/heaps/system_heap.c-269-\ndrivers/dma-buf/heaps/system_heap.c:270:static int system_heap_vmap(struct dma_buf *dmabuf, struct iosys_map *map)\ndrivers/dma-buf/heaps/system_heap.c-271-{\ndrivers/dma-buf/heaps/system_heap.c:272:\tstruct system_heap_buffer *buffer = dmabuf-\u003epriv;\ndrivers/dma-buf/heaps/system_heap.c-273-\tvoid *vaddr;\n--\ndrivers/dma-buf/heaps/system_heap.c-282-\ndrivers/dma-buf/heaps/system_heap.c:283:\tvaddr = system_heap_do_vmap(buffer);\ndrivers/dma-buf/heaps/system_heap.c-284-\tif (IS_ERR(vaddr)) {\n--\ndrivers/dma-buf/heaps/system_heap.c-297-\ndrivers/dma-buf/heaps/system_heap.c:298:static void system_heap_vunmap(struct dma_buf *dmabuf, struct iosys_map *map)\ndrivers/dma-buf/heaps/system_heap.c-299-{\ndrivers/dma-buf/heaps/system_heap.c:300:\tstruct system_heap_buffer *buffer = dmabuf-\u003epriv;\ndrivers/dma-buf/heaps/system_heap.c-301-\n--\ndrivers/dma-buf/heaps/system_heap.c-310-\ndrivers/dma-buf/heaps/system_heap.c:311:static void system_heap_free_page(struct page *page, bool cc_shared)\ndrivers/dma-buf/heaps/system_heap.c-312-{\n--\ndrivers/dma-buf/heaps/system_heap.c-324-\ndrivers/dma-buf/heaps/system_heap.c:325:static void system_heap_dma_buf_release(struct dma_buf *dmabuf)\ndrivers/dma-buf/heaps/system_heap.c-326-{\ndrivers/dma-buf/heaps/system_heap.c:327:\tstruct system_heap_buffer *buffer = dmabuf-\u003epriv;\ndrivers/dma-buf/heaps/system_heap.c-328-\tstruct sg_table *table;\n--\ndrivers/dma-buf/heaps/system_heap.c-333-\tfor_each_sgtable_sg(table, sg, i)\ndrivers/dma-buf/heaps/system_heap.c:334:\t\tsystem_heap_free_page(sg_page(sg), cc_shared_buffer(buffer));\ndrivers/dma-buf/heaps/system_heap.c-335-\tsg_free_table(table);\n--\ndrivers/dma-buf/heaps/system_heap.c-338-\ndrivers/dma-buf/heaps/system_heap.c:339:static const struct dma_buf_ops system_heap_buf_ops = {\ndrivers/dma-buf/heaps/system_heap.c:340:\t.attach = system_heap_attach,\ndrivers/dma-buf/heaps/system_heap.c:341:\t.detach = system_heap_detach,\ndrivers/dma-buf/heaps/system_heap.c:342:\t.map_dma_buf = system_heap_map_dma_buf,\ndrivers/dma-buf/heaps/system_heap.c:343:\t.unmap_dma_buf = system_heap_unmap_dma_buf,\ndrivers/dma-buf/heaps/system_heap.c:344:\t.begin_cpu_access = system_heap_dma_buf_begin_cpu_access,\ndrivers/dma-buf/heaps/system_heap.c:345:\t.end_cpu_access = system_heap_dma_buf_end_cpu_access,\ndrivers/dma-buf/heaps/system_heap.c:346:\t.mmap = system_heap_mmap,\ndrivers/dma-buf/heaps/system_heap.c:347:\t.vmap = system_heap_vmap,\ndrivers/dma-buf/heaps/system_heap.c:348:\t.vunmap = system_heap_vunmap,\ndrivers/dma-buf/heaps/system_heap.c:349:\t.release = system_heap_dma_buf_release,\ndrivers/dma-buf/heaps/system_heap.c-350-};\ndrivers/dma-buf/heaps/system_heap.c-351-\ndrivers/dma-buf/heaps/system_heap.c:352:static struct page *system_heap_alloc_order(unsigned int order,\ndrivers/dma-buf/heaps/system_heap.c-353-\t     gfp_t flags, bool cc_shared)\n--\ndrivers/dma-buf/heaps/system_heap.c=368=static struct page *alloc_largest_available(unsigned long size,\n--\ndrivers/dma-buf/heaps/system_heap.c-383-\t\t\tflags |= __GFP_ACCOUNT;\ndrivers/dma-buf/heaps/system_heap.c:384:\t\tpage = system_heap_alloc_order(orders[i], flags, cc_shared);\ndrivers/dma-buf/heaps/system_heap.c-385-\t\tif (!page)\n--\ndrivers/dma-buf/heaps/system_heap.c-391-\ndrivers/dma-buf/heaps/system_heap.c:392:static struct dma_buf *system_heap_allocate(struct dma_heap *heap,\ndrivers/dma-buf/heaps/system_heap.c-393-\t\t\t\t\t    unsigned long len,\n--\ndrivers/dma-buf/heaps/system_heap.c-396-{\ndrivers/dma-buf/heaps/system_heap.c:397:\tstruct system_heap_buffer *buffer;\ndrivers/dma-buf/heaps/system_heap.c-398-\tDEFINE_DMA_BUF_EXPORT_INFO(exp_info);\n--\ndrivers/dma-buf/heaps/system_heap.c-402-\tunsigned int max_order = orders[0];\ndrivers/dma-buf/heaps/system_heap.c:403:\tstruct system_heap_priv *priv = dma_heap_get_drvdata(heap);\ndrivers/dma-buf/heaps/system_heap.c-404-\tbool cc_shared = priv-\u003ecc_shared;\n--\ndrivers/dma-buf/heaps/system_heap.c-471-\texp_info.exp_name = dma_heap_get_name(heap);\ndrivers/dma-buf/heaps/system_heap.c:472:\texp_info.ops = \u0026system_heap_buf_ops;\ndrivers/dma-buf/heaps/system_heap.c-473-\texp_info.size = buffer-\u003elen;\n--\ndrivers/dma-buf/heaps/system_heap.c-484-\tfor_each_sgtable_sg(table, sg, i)\ndrivers/dma-buf/heaps/system_heap.c:485:\t\tsystem_heap_free_page(sg_page(sg), cc_shared);\ndrivers/dma-buf/heaps/system_heap.c-486-\tsg_free_table(table);\n--\ndrivers/dma-buf/heaps/system_heap.c-488-\tlist_for_each_entry_safe(page, tmp_page, \u0026pages, lru)\ndrivers/dma-buf/heaps/system_heap.c:489:\t\tsystem_heap_free_page(page, cc_shared);\ndrivers/dma-buf/heaps/system_heap.c-490-\tkfree(buffer);\n--\ndrivers/dma-buf/heaps/system_heap.c-494-\ndrivers/dma-buf/heaps/system_heap.c:495:static const struct dma_heap_ops system_heap_ops = {\ndrivers/dma-buf/heaps/system_heap.c:496:\t.allocate = system_heap_allocate,\ndrivers/dma-buf/heaps/system_heap.c-497-};\ndrivers/dma-buf/heaps/system_heap.c-498-\ndrivers/dma-buf/heaps/system_heap.c:499:static struct system_heap_priv system_heap_priv = {\ndrivers/dma-buf/heaps/system_heap.c-500-\t.cc_shared = false,\n--\ndrivers/dma-buf/heaps/system_heap.c-502-\ndrivers/dma-buf/heaps/system_heap.c:503:static struct system_heap_priv system_heap_cc_shared_priv = {\ndrivers/dma-buf/heaps/system_heap.c-504-\t.cc_shared = true,\n--\ndrivers/dma-buf/heaps/system_heap.c-506-\ndrivers/dma-buf/heaps/system_heap.c:507:static int __init system_heap_create(void)\ndrivers/dma-buf/heaps/system_heap.c-508-{\n--\ndrivers/dma-buf/heaps/system_heap.c-512-\texp_info.name = \"system\";\ndrivers/dma-buf/heaps/system_heap.c:513:\texp_info.ops = \u0026system_heap_ops;\ndrivers/dma-buf/heaps/system_heap.c:514:\texp_info.priv = \u0026system_heap_priv;\ndrivers/dma-buf/heaps/system_heap.c-515-\n--\ndrivers/dma-buf/heaps/system_heap.c-525-\texp_info.name = \"system_cc_shared\";\ndrivers/dma-buf/heaps/system_heap.c:526:\texp_info.priv = \u0026system_heap_cc_shared_priv;\ndrivers/dma-buf/heaps/system_heap.c-527-\tsys_heap = dma_heap_add(\u0026exp_info);\n--\ndrivers/dma-buf/heaps/system_heap.c-532-}\ndrivers/dma-buf/heaps/system_heap.c:533:module_init(system_heap_create);\ndrivers/dma-buf/heaps/system_heap.c-534-\n"
}

41/2 2026/09/26 15:31 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 82575
							output: 33
							thoughts: 140
						

Thoughts:
Okay, let's break this down.

**Configuration Essentials for DMA Buffer Heaps**

Right, this is straightforward. The key here is the `system_heap.c` configuration. I need to make sure `CONFIG_DMABUF_HEAPS_SYSTEM` and `CONFIG_DMABUF_HEAPS` are enabled. And, if I'm specifically testing the cc shared heap scenario, then I also need to ensure that `CONFIG_DMABUF_HEAPS_SYSTEM_CC_SHARED` is set appropriately. Moving on to `contiguous.c`, `CONFIG_DMA_CMA` and `CONFIG_CMA` are the configurations I must review. Finally, for `pool.c`, I'm looking for `CONFIG_DMA_COHERENT_POOL`.

Now, before I proceed any further, I should double-check the Kconfig files for both `DMA_CMA` and `DMA_COHERENT_POOL` to ensure there are no dependencies or specific settings that I need to be aware of. Gotta cover all the bases.



42/2 2026/09/26 15:31 tool grepper
0m Args:
{
  "Expression": "config DMA_COHERENT_POOL",
  "PathPrefix": "kernel/dma/"
}

Results:
{
  "Output": "kernel/dma/Kconfig=151=config DMA_NONCOHERENT_MMAP\n--\nkernel/dma/Kconfig-154-\nkernel/dma/Kconfig:155:config DMA_COHERENT_POOL\nkernel/dma/Kconfig-156-\tselect GENERIC_ALLOCATOR\n"
}

43/2 2026/09/26 15:31 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 83065
							output: 35
							thoughts: 25
						

Thoughts:
Here's my analysis of the provided snippet, tailored for an expert audience and presented as if it were my internal thought process:

**Diving into DMA Configuration**

Okay, so we're looking at a potential issue related to DMA configuration. My first move here is to zero in on `kernel/dma/Kconfig`. That's where the heart of the DMA configuration options reside. Specifically, I need to check around lines 155 and 179. Those areas tend to house the more interesting, and often the more problematic, configuration switches – typically the ones that enable or disable certain DMA features or platform-specific DMA controllers. I need to see what's being defined or guarded around those lines. My initial assumption is that we are looking for a bug or issue, so these lines must be critical. I'm going to look for any dependencies or conditional statements that might be causing a particular DMA controller, or feature set, to be incorrectly enabled, disabled, or not functioning as expected given the current hardware configuration. Let's dig in and see what's being controlled.



44/2 2026/09/26 15:31 tool read-file
0m Args:
{
  "File": "kernel/dma/Kconfig",
  "FirstLine": 150,
  "LineCount": 45
}

Results:
{
  "Contents": " 150:\t#\n 151:\tconfig DMA_NONCOHERENT_MMAP\n 152:\t\tdefault y if !MMU\n 153:\t\tbool\n 154:\t\n 155:\tconfig DMA_COHERENT_POOL\n 156:\t\tselect GENERIC_ALLOCATOR\n 157:\t\tbool\n 158:\t\n 159:\tconfig DMA_GLOBAL_POOL\n 160:\t\tselect DMA_DECLARE_COHERENT\n 161:\t\tdepends on !ARCH_HAS_DMA_SET_UNCACHED\n 162:\t\tdepends on !DMA_DIRECT_REMAP\n 163:\t\tbool\n 164:\t\n 165:\tconfig DMA_DIRECT_REMAP\n 166:\t\tbool\n 167:\t\tselect DMA_COHERENT_POOL\n 168:\t\tselect DMA_NONCOHERENT_MMAP\n 169:\t\n 170:\t#\n 171:\t# Fallback to arch code for DMA allocations.  This should eventually go away.\n 172:\t#\n 173:\tconfig ARCH_HAS_DMA_ALLOC\n 174:\t\tdepends on !ARCH_HAS_DMA_SET_UNCACHED\n 175:\t\tdepends on !DMA_DIRECT_REMAP\n 176:\t\tdepends on !DMA_GLOBAL_POOL\n 177:\t\tbool\n 178:\t\n 179:\tconfig DMA_CMA\n 180:\t\tbool \"DMA Contiguous Memory Allocator\"\n 181:\t\tdepends on HAVE_DMA_CONTIGUOUS \u0026\u0026 CMA\n 182:\t\thelp\n 183:\t\t  This enables the Contiguous Memory Allocator which allows drivers\n 184:\t\t  to allocate big physically-contiguous blocks of memory for use with\n 185:\t\t  hardware components that do not support I/O map nor scatter-gather.\n 186:\t\n 187:\t\t  You can disable CMA by specifying \"cma=0\" on the kernel's command\n 188:\t\t  line.\n 189:\t\n 190:\t\t  For more information see \u003ckernel/dma/contiguous.c\u003e.\n 191:\t\t  If unsure, say \"n\".\n 192:\t\n 193:\tif  DMA_CMA\n 194:\t\n"
}

45/2 2026/09/26 15:31 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 83957
							output: 33
							thoughts: 726
						

Thoughts:
**My Analysis of Relevant Kernel Configurations and Functions**

Okay, let's break this down systematically. My primary goal is to identify the most relevant kernel configurations and specific functions that have been modified or implement the altered logic. I'm focusing on non-ubiquitous changes to avoid diluting the scope.

First, I need to map the dependencies: `DMA_CMA` relies on `HAVE_DMA_CONTIGUOUS` and `CMA`. `DMA_COHERENT_POOL` is selected by `DMA_DIRECT_REMAP`. Then we have `DMABUF_HEAPS_SYSTEM` which depends on `DMABUF_HEAPS`. And `DMABUF_HEAPS_SYSTEM_CC_SHARED` has a more complex dependency: `DMABUF_HEAPS && ARCH_HAS_MEM_ENCRYPT && DMABUF_HEAPS_SYSTEM=y`. This means confidential shared memory options are in play.

Now, let's drill into the key functions that are involved in the changes:

*   `system_heap_allocate`: This is crucial. It handles allocations from `/dev/dma_heap/system`. This is quite specific, and I need to investigate the modifications to see how it handles `sg_len` calculation and the new allocation helper.
*   `system_heap_dma_buf_release`: This function is tied to releasing system heap dma\_bufs. I'll need to see how the call to `system_heap_free_page` is handled.
*   `dma_alloc_contiguous`: This function has been modified to accept `attrs`, calculate alignment including confidential shared requirements, and call `cma_alloc_aligned`. This is a CMA function, which makes it less ubiquitous.
*   `dma_alloc_from_contiguous`: This is another CMA-related function that has been modified similarly to `dma_alloc_contiguous`.
*   `cma_alloc_aligned`: This helper function, static within `kernel/dma/contiguous.c`, is modified to accept `align` directly.
*   `atomic_pool_expand`: The changes here focus on calculating the layout and min\_order for atomic DMA pools.

I've already assessed that none of these are terribly ubiquitous. `system_heap_allocate` and `system_heap_dma_buf_release` have a limited scope. `dma_alloc_contiguous`, `dma_alloc_from_contiguous`, and `cma_alloc_aligned` are clearly CMA-focused, while `atomic_pool_expand` is called only during atomic pool expansion.

I need to confirm if `dma_direct_alloc` should be included. No, this function is a generic hotpath, which would dilute the coverage, so `dma_alloc_contiguous` and `dma_alloc_from_contiguous` are better targets.

So, let's enumerate the EnableConfigs: `DMABUF_HEAPS`, `DMABUF_HEAPS_SYSTEM`, `DMABUF_HEAPS_SYSTEM_CC_SHARED`, `DMA_CMA`, and `CMA`.

Finally, since we are dealing with confidential shared memory, I must check `CONFIG_AMD_MEM_ENCRYPT`. The dependencies suggest this might be a factor, so I should look into `arch/x86/Kconfig` for `AMD_MEM_ENCRYPT`. I need to ensure I cover any relevant conditional compilation that this may invoke.



46/2 2026/09/26 15:31 tool grepper
0m Args:
{
  "Expression": "config AMD_MEM_ENCRYPT",
  "PathPrefix": "arch/x86/"
}

Results:
{
  "Output": "arch/x86/Kconfig=1487=config X86_MEM_ENCRYPT\n--\narch/x86/Kconfig-1491-\narch/x86/Kconfig:1492:config AMD_MEM_ENCRYPT\narch/x86/Kconfig-1493-\tbool \"AMD Secure Memory Encryption (SME) support\"\n"
}

47/2 2026/09/26 15:31 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 85564
							output: 37
							thoughts: 21
						

Thoughts:
Here's my thought process, as if I were considering that code:

**Diving into the x86 Architecture Configuration**

Okay, I need to investigate something related to the x86 architecture configuration. Based on my current line of inquiry, let's head straight to `arch/x86/Kconfig` around line 1485. I'm expecting something significant to be there, perhaps a conditional compilation option, a hardware feature definition, or maybe a dependency check. I need to understand the implications of what's defined there in relation to what I'm working on. The exact line number is more of a starting point than a hard target, but it gives me a clear direction to begin my analysis within that specific Kconfig file. I'll need to rapidly parse the surrounding context to understand the scope and intent of that specific section. Hopefully, I'll quickly see the connection to the issue at hand.



48/2 2026/09/26 15:31 tool read-file
0m Args:
{
  "File": "arch/x86/Kconfig",
  "FirstLine": 1485,
  "LineCount": 30
}

Results:
{
  "Contents": "1485:\t\t  page mappings when mapping protections are changed.\n1486:\t\n1487:\tconfig X86_MEM_ENCRYPT\n1488:\t\tselect ARCH_HAS_FORCE_DMA_UNENCRYPTED\n1489:\t\tselect DYNAMIC_PHYSICAL_MASK\n1490:\t\tdef_bool n\n1491:\t\n1492:\tconfig AMD_MEM_ENCRYPT\n1493:\t\tbool \"AMD Secure Memory Encryption (SME) support\"\n1494:\t\tdepends on X86_64 \u0026\u0026 CPU_SUP_AMD\n1495:\t\tdepends on EFI_STUB\n1496:\t\tselect DMA_COHERENT_POOL\n1497:\t\tselect ARCH_USE_MEMREMAP_PROT\n1498:\t\tselect INSTRUCTION_DECODER\n1499:\t\tselect ARCH_HAS_CC_PLATFORM\n1500:\t\tselect X86_MEM_ENCRYPT\n1501:\t\tselect UNACCEPTED_MEMORY\n1502:\t\tselect CRYPTO_LIB_AES_GCM\n1503:\t\thelp\n1504:\t\t  Say yes to enable support for the encryption of system memory.\n1505:\t\t  This requires an AMD processor that supports Secure Memory\n1506:\t\t  Encryption (SME).\n1507:\t\n1508:\t# Common NUMA Features\n1509:\tconfig NUMA\n1510:\t\tbool \"NUMA Memory Allocation and Scheduler Support\"\n1511:\t\tdepends on SMP\n1512:\t\tdepends on X86_64\n1513:\t\tselect USE_PERCPU_NUMA_NODE_ID\n1514:\t\tselect OF_NUMA if OF\n"
}

49/2 2026/09/26 15:31 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 86259
							output: 34
							thoughts: 76
						

Thoughts:
Okay, here's my interpretation of these thoughts, formatted as you requested, as if they were my own:

**Analyzing Memory Encryption and DMA Coherency**

Hmm, interesting observation! It seems like enabling `AMD_MEM_ENCRYPT` is automatically selecting `DMA_COHERENT_POOL`. That's a key detail to note. And further, it appears that `AMD_MEM_ENCRYPT` also triggers the enabling of `CONFIG_ARCH_HAS_MEM_ENCRYPT`, which in turn unlocks the option for `DMABUF_HEAPS_SYSTEM_CC_SHARED`. I should remember that linking between `AMD_MEM_ENCRYPT`, `CONFIG_ARCH_HAS_MEM_ENCRYPT` and `DMABUF_HEAPS_SYSTEM_CC_SHARED` may be useful for further optimization or debugging. Time to verify how `ARCH_HAS_MEM_ENCRYPT` is defined or utilized. Let me check that next...



50/2 2026/09/26 15:31 tool grepper
0m Args:
{
  "Expression": "ARCH_HAS_MEM_ENCRYPT",
  "PathPrefix": "arch/x86/"
}

Results:
{
  "Output": "arch/x86/Kconfig=58=config X86\n--\narch/x86/Kconfig-93-\tselect ARCH_HAS_KERNEL_FPU_SUPPORT\narch/x86/Kconfig:94:\tselect ARCH_HAS_MEM_ENCRYPT\narch/x86/Kconfig-95-\tselect ARCH_HAS_MEMBARRIER_SYNC_CORE\n"
}

51/2 2026/09/26 15:31 llm patch-evaluator
1m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 86675
							output: 222
							thoughts: 399
						

Thoughts:
**Analysis of Configuration and Focus for Confidential Computing on x86**

Okay, let's break this down. On x86, the presence of `ARCH_HAS_MEM_ENCRYPT` is contingent on `config X86`, as we know. Given that, we need to ensure the following configurations are enabled: `DMABUF_HEAPS`, `DMABUF_HEAPS_SYSTEM`, `DMABUF_HEAPS_SYSTEM_CC_SHARED`, `DMA_CMA`, `CMA`, and `AMD_MEM_ENCRYPT`.

Next, I need to check the critical symbols involved. The ones that immediately stand out as important areas of focus are: `system_heap_allocate`, `system_heap_dma_buf_release`, `dma_alloc_contiguous`, `dma_alloc_from_contiguous`, and `atomic_pool_expand`. These functions are central to memory allocation, management, and DMA operations, all of which are relevant to confidential computing.

Now, let's cross-reference and double check everything, and ensure it's all aligned before going further. The reasoning is concise and accurate: the patch series introduces shared page allocation and alignment abstractions for confidential computing and refactors allocation/freeing logic within the DMA contiguous memory allocator (CMA), DMA pool allocator, and the DMA-buf system heap (`drivers/dma-buf/heaps/system_heap.c`). It's directly modifying code accessible on amd64 in virtualized environments, and crucially touching user-accessible system heap buffer allocation/release routines along with contiguous DMA allocations.

All aspects are solid and verified: `WorthFuzzing` is set to true, the reasoning is sound, and all enablement configurations and focus symbols are correct. I'm satisfied. Now it's time to invoke `set-results`.



52/2 2026/09/26 15:32 tool set-results
0m Args:
{
  "EnableConfigs": [
    "DMABUF_HEAPS",
    "DMABUF_HEAPS_SYSTEM",
    "DMABUF_HEAPS_SYSTEM_CC_SHARED",
    "DMA_CMA",
    "CMA",
    "AMD_MEM_ENCRYPT"
  ],
  "FocusSymbols": [
    "system_heap_allocate",
    "system_heap_dma_buf_release",
    "dma_alloc_contiguous",
    "dma_alloc_from_contiguous",
    "atomic_pool_expand"
  ],
  "Reasoning": "The patch series introduces shared page allocation and alignment helpers for confidential computing, while refactoring memory allocation and release paths across the DMA contiguous memory allocator (CMA), atomic DMA pools, and the DMA-buf system heap (drivers/dma-buf/heaps/system_heap.c). These changes modify reachable code paths on amd64, including user-accessible dma_heap allocation/release logic and CMA buffer allocation.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "DMABUF_HEAPS",
    "DMABUF_HEAPS_SYSTEM",
    "DMABUF_HEAPS_SYSTEM_CC_SHARED",
    "DMA_CMA",
    "CMA",
    "AMD_MEM_ENCRYPT"
  ],
  "FocusSymbols": [
    "system_heap_allocate",
    "system_heap_dma_buf_release",
    "dma_alloc_contiguous",
    "dma_alloc_from_contiguous",
    "atomic_pool_expand"
  ],
  "Reasoning": "The patch series introduces shared page allocation and alignment helpers for confidential computing, while refactoring memory allocation and release paths across the DMA contiguous memory allocator (CMA), atomic DMA pools, and the DMA-buf system heap (drivers/dma-buf/heaps/system_heap.c). These changes modify reachable code paths on amd64, including user-accessible dma_heap allocation/release logic and CMA buffer allocation.",
  "WorthFuzzing": true
}

53/1 2026/09/26 15:32 action If
3m Args:
{
  "WorthFuzzing": true
}

Results:
null

54/2 2026/09/26 15:32 agent kmsan-evaluator
3m Model:
core

Args:
null

Results:
{
  "KMSANReasoning": "The patch series consolidates confidential computing (CC) shared/decrypted memory management and pushes zeroing into architecture-specific `set_memory_decrypted()` hooks. However, in `drivers/hv/hv.c:hv_alloc_page()`, the patch changes the unconditional `memset(*page, 0, PAGE_SIZE)` to only execute in the `else` branch of `if (decrypt)`:\n\n```c\n\tif (decrypt) {\n\t\tret = set_memory_decrypted((unsigned long)*page, 1);\n\t\tif (ret)\n\t\t\tgoto failed;\n\t} else {\n\t\tmemset(*page, 0, PAGE_SIZE);\n\t}\n```\n\nIn standard non-confidential Hyper-V guest VMs, `vmbus_is_confidential()` returns `false`, causing `hv_synic_alloc()` to set `decrypt = true`. On such systems, `cc_platform_has(CC_ATTR_MEM_ENCRYPT)` is false, meaning `set_memory_decrypted()` is a no-op that returns 0 without clearing the page. Consequently, `hv_alloc_page()` leaves `hyp_synic_message_page` and `hyp_synic_event_page` allocated via `__get_free_page(GFP_KERNEL)` completely uninitialized.\n\nWhen Hyper-V message DPC handling runs (`vmbus_drv.c:__vmbus_on_msg_dpc`), the kernel reads `msg = (struct hv_message *)message_page_addr + VMBUS_MESSAGE_SINT;` and branches on `msg_copy.header.message_type == HVMSG_NONE` and subsequent channel message headers using uninitialized memory.\n\nKASAN only tracks address bounds and object lifetime (use-after-free, out-of-bounds); because the allocated page is a valid memory object, KASAN cannot detect reads from uninitialized page contents or conditional logic branching on uninitialized data. Only KMSAN tracks page initialization shadow state and detects control flow / logic dependencies on uninitialized memory. Thus, a dedicated KMSAN fuzzing session is justified.",
  "NeedsKMSAN": true
}

Instruction:
You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.

CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
  a comprehensive suite of debugging tools and sanitizers, including KASAN
  (out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
  (locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
  or page allocations) and kernel-to-user memory info-leaks.

Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.

Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.

Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
   attributes, ioctl output arguments, socket options, or BPF buffers) where fields
   or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
   or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
   could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
   uninitialized bytes of existing buffers.

Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
  dereferences, locking deadlocks, or use-after-free bugs (these are already caught
  by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
  or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.

Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit 4a46b87645d47c6cdb58e9df76e3a9445bb4efbd
Author: syz-cluster <triage@syzkaller.com>
Date:   Sat Sep 26 15:28:34 2026 +0000

    syz-cluster: applied patch under review

diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index 7761099dde9e8..9714fcd519418 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -398,7 +398,8 @@ static void *__alloc_from_contiguous(struct device *dev, size_t size,
 	struct page *page;
 	void *ptr = NULL;
 
-	page = dma_alloc_from_contiguous(dev, count, order, gfp & __GFP_NOWARN);
+	page = dma_alloc_from_contiguous(dev, count, order, 0,
+					 gfp & __GFP_NOWARN);
 	if (!page)
 		return NULL;
 
@@ -866,7 +867,7 @@ static struct page **__iommu_alloc_buffer(struct device *dev, size_t size,
 		unsigned long order = get_order(size);
 		struct page *page;
 
-		page = dma_alloc_from_contiguous(dev, count, order,
+		page = dma_alloc_from_contiguous(dev, count, order, 0,
 						 gfp & __GFP_NOWARN);
 		if (!page)
 			goto error;
diff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c
index bbe98ac9ad8c6..ae1f5de66fad7 100644
--- a/arch/arm64/mm/pageattr.c
+++ b/arch/arm64/mm/pageattr.c
@@ -339,6 +339,9 @@ static int realm_set_memory_decrypted(unsigned long addr, int numpages)
 {
 	int ret = __set_memory_enc_dec(addr, numpages, false);
 
+	if (!ret)
+		memset((void *)addr, 0, (size_t)numpages << PAGE_SHIFT);
+
 	WARN(ret, "Failed to decrypt memory, %d pages will be leaked",
 	     numpages);
 
diff --git a/arch/powerpc/platforms/pseries/svm.c b/arch/powerpc/platforms/pseries/svm.c
index 7a403dbd35ee5..a344e094fe1c8 100644
--- a/arch/powerpc/platforms/pseries/svm.c
+++ b/arch/powerpc/platforms/pseries/svm.c
@@ -9,6 +9,7 @@
 #include <linux/mm.h>
 #include <linux/memblock.h>
 #include <linux/mem_encrypt.h>
+#include <linux/string.h>
 #include <linux/cc_platform.h>
 #include <asm/machdep.h>
 #include <asm/svm.h>
@@ -59,6 +60,7 @@ int set_memory_decrypted(unsigned long addr, int numpages)
 	if (!PAGE_ALIGNED(addr))
 		return -EINVAL;
 
+	memset((void *)addr, 0, (size_t)numpages << PAGE_SHIFT);
 	uv_share_page(PHYS_PFN(__pa(addr)), numpages);
 
 	return 0;
diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c
index be7e009e7b59e..f2e28a2710e04 100644
--- a/arch/s390/mm/init.c
+++ b/arch/s390/mm/init.c
@@ -129,6 +129,9 @@ int set_memory_encrypted(unsigned long vaddr, int numpages)
 int set_memory_decrypted(unsigned long vaddr, int numpages)
 {
 	int i;
+
+	memset((void *)vaddr, 0, (size_t)numpages << PAGE_SHIFT);
+
 	/* make specified pages shared (swiotlb, dma_alloca) */
 	for (i = 0; i < numpages; ++i) {
 		uv_set_shared(virt_to_phys((void *)vaddr));
diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c
index f904a636d449b..1f1f390823911 100644
--- a/arch/x86/coco/tdx/tdx.c
+++ b/arch/x86/coco/tdx/tdx.c
@@ -978,6 +978,9 @@ static bool tdx_enc_status_changed(unsigned long vaddr, int numpages, bool enc)
 static int tdx_enc_status_change_prepare(unsigned long vaddr, int numpages,
 					 bool enc)
 {
+	if (!enc)
+		memset((void *)vaddr, 0, (size_t)numpages << PAGE_SHIFT);
+
 	/*
 	 * Only handle shared->private conversion here.
 	 * See the comment in tdx_early_init().
diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c
index 0b4a1c0b0b16f..a42468e81c344 100644
--- a/arch/x86/hyperv/hv_init.c
+++ b/arch/x86/hyperv/hv_init.c
@@ -156,8 +156,10 @@ static int hv_cpu_init(unsigned int cpu)
 			 * page in non-root partition here.
 			 */
 			if (*hvp && !ms_hyperv.paravisor_present && hv_isolation_type_snp()) {
-				WARN_ON_ONCE(set_memory_decrypted((unsigned long)(*hvp), 1));
-				memset(*hvp, 0, PAGE_SIZE);
+				int ret;
+
+				ret = set_memory_decrypted((unsigned long)*hvp, 1);
+				WARN_ON_ONCE(ret);
 			}
 		}
 
diff --git a/arch/x86/hyperv/ivm.c b/arch/x86/hyperv/ivm.c
index 2ce4dfe534727..e9c7799a00943 100644
--- a/arch/x86/hyperv/ivm.c
+++ b/arch/x86/hyperv/ivm.c
@@ -755,6 +755,10 @@ static int hv_mark_gpa_visibility(u16 count, const u64 pfn[],
  */
 static int hv_vtom_clear_present(unsigned long kbuffer, int pagecount, bool enc)
 {
+	if (!enc)
+		memset((void *)kbuffer, 0,
+		       (size_t)pagecount << PAGE_SHIFT);
+
 	return set_memory_np(kbuffer, pagecount);
 }
 
diff --git a/arch/x86/kernel/kvmclock.c b/arch/x86/kernel/kvmclock.c
index cb3d0ca1fa225..3e9b7ad3dea2b 100644
--- a/arch/x86/kernel/kvmclock.c
+++ b/arch/x86/kernel/kvmclock.c
@@ -248,7 +248,7 @@ static void __init kvmclock_init_mem(void)
 	 * be mapped decrypted.
 	 */
 	if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) {
-		r = set_memory_decrypted((unsigned long) hvclock_mem,
+		r = set_memory_decrypted((unsigned long)hvclock_mem,
 					 1UL << order);
 		if (r) {
 			__free_pages(p, order);
@@ -256,9 +256,9 @@ static void __init kvmclock_init_mem(void)
 			pr_warn("kvmclock: set_memory_decrypted() failed. Disabling\n");
 			return;
 		}
+	} else {
+		memset(hvclock_mem, 0, PAGE_SIZE << order);
 	}
-
-	memset(hvclock_mem, 0, PAGE_SIZE << order);
 }
 
 static int __init kvm_setup_vsyscall_timeinfo(void)
diff --git a/arch/x86/mm/mem_encrypt_amd.c b/arch/x86/mm/mem_encrypt_amd.c
index 2f8c32173972d..47cae102acd24 100644
--- a/arch/x86/mm/mem_encrypt_amd.c
+++ b/arch/x86/mm/mem_encrypt_amd.c
@@ -13,6 +13,7 @@
 #include <linux/dma-direct.h>
 #include <linux/swiotlb.h>
 #include <linux/mem_encrypt.h>
+#include <linux/string.h>
 #include <linux/device.h>
 #include <linux/kernel.h>
 #include <linux/bitops.h>
@@ -285,6 +286,9 @@ static void enc_dec_hypercall(unsigned long vaddr, unsigned long size, bool enc)
 
 static int amd_enc_status_change_prepare(unsigned long vaddr, int npages, bool enc)
 {
+	if (!enc)
+		memset((void *)vaddr, 0, (size_t)npages << PAGE_SHIFT);
+
 	/*
 	 * To maintain the security guarantees of SEV-SNP guests, make sure
 	 * to invalidate the memory before encryption attribute is cleared.
diff --git a/drivers/dma-buf/heaps/system_heap.c b/drivers/dma-buf/heaps/system_heap.c
index c8959eadc71d1..5970b76416ae5 100644
--- a/drivers/dma-buf/heaps/system_heap.c
+++ b/drivers/dma-buf/heaps/system_heap.c
@@ -11,14 +11,13 @@
  */
 
 #include <linux/cc_platform.h>
+#include <linux/cc_shared.h>
 #include <linux/dma-buf.h>
 #include <linux/dma-mapping.h>
 #include <linux/dma-heap.h>
 #include <linux/err.h>
 #include <linux/highmem.h>
-#include <linux/mem_encrypt.h>
 #include <linux/mm.h>
-#include <linux/set_memory.h>
 #include <linux/module.h>
 #include <linux/pgtable.h>
 #include <linux/scatterlist.h>
@@ -65,34 +64,6 @@ static gfp_t order_flags[] = {HIGH_ORDER_GFP, HIGH_ORDER_GFP, LOW_ORDER_GFP};
 static const unsigned int orders[] = {8, 4, 0};
 #define NUM_ORDERS ARRAY_SIZE(orders)
 
-static int system_heap_set_page_decrypted(struct page *page)
-{
-	unsigned long addr = (unsigned long)page_address(page);
-	unsigned int nr_pages = 1 << compound_order(page);
-	int ret;
-
-	ret = set_memory_decrypted(addr, nr_pages);
-	if (ret)
-		pr_warn_ratelimited("dma-buf system heap: failed to decrypt page at %p\n",
-				    page_address(page));
-
-	return ret;
-}
-
-static int system_heap_set_page_encrypted(struct page *page)
-{
-	unsigned long addr = (unsigned long)page_address(page);
-	unsigned int nr_pages = 1 << compound_order(page);
-	int ret;
-
-	ret = set_memory_encrypted(addr, nr_pages);
-	if (ret)
-		pr_warn_ratelimited("dma-buf system heap: failed to re-encrypt page at %p, leaking memory\n",
-				    page_address(page));
-
-	return ret;
-}
-
 static int dup_sg_table(struct sg_table *from, struct sg_table *to)
 {
 	struct scatterlist *sg, *new_sg;
@@ -337,6 +308,20 @@ static void system_heap_vunmap(struct dma_buf *dmabuf, struct iosys_map *map)
 	iosys_map_clear(map);
 }
 
+static void system_heap_free_page(struct page *page, bool cc_shared)
+{
+	struct cc_shared_pages mem;
+
+	if (!cc_shared) {
+		__free_pages(page, compound_order(page));
+		return;
+	}
+
+	mem.page = page;
+	mem.shared_size = page_size(page);
+	free_cc_shared_pages(&mem);
+}
+
 static void system_heap_dma_buf_release(struct dma_buf *dmabuf)
 {
 	struct system_heap_buffer *buffer = dmabuf->priv;
@@ -345,19 +330,8 @@ static void system_heap_dma_buf_release(struct dma_buf *dmabuf)
 	int i;
 
 	table = &buffer->sg_table;
-	for_each_sgtable_sg(table, sg, i) {
-		struct page *page = sg_page(sg);
-
-		/*
-		 * Intentionally leak pages that cannot be re-encrypted
-		 * to prevent shared memory from being reused.
-		 */
-		if (cc_shared_buffer(buffer) &&
-		    system_heap_set_page_encrypted(page))
-			continue;
-
-		__free_pages(page, compound_order(page));
-	}
+	for_each_sgtable_sg(table, sg, i)
+		system_heap_free_page(sg_page(sg), cc_shared_buffer(buffer));
 	sg_free_table(table);
 	kfree(buffer);
 }
@@ -375,22 +349,39 @@ static const struct dma_buf_ops system_heap_buf_ops = {
 	.release = system_heap_dma_buf_release,
 };
 
+static struct page *system_heap_alloc_order(unsigned int order,
+	     gfp_t flags, bool cc_shared)
+{
+	struct cc_shared_pages mem;
+
+	if (!cc_shared)
+		return alloc_pages(flags, order);
+
+	/* The shared granule can raise the actual allocation order. */
+	flags |= __GFP_COMP;
+	if (alloc_cc_shared_pages(flags, PAGE_SIZE << order, &mem))
+		return NULL;
+
+	return mem.page;
+}
+
 static struct page *alloc_largest_available(unsigned long size,
-					    unsigned int max_order)
+		unsigned int max_order, bool cc_shared)
 {
 	struct page *page;
-	int i;
 	gfp_t flags;
+	int i;
 
 	for (i = 0; i < NUM_ORDERS; i++) {
 		if (size <  (PAGE_SIZE << orders[i]))
 			continue;
 		if (max_order < orders[i])
 			continue;
+
 		flags = order_flags[i];
 		if (mem_accounting)
 			flags |= __GFP_ACCOUNT;
-		page = alloc_pages(flags, orders[i]);
+		page = system_heap_alloc_order(orders[i], flags, cc_shared);
 		if (!page)
 			continue;
 		return page;
@@ -405,7 +396,9 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
 {
 	struct system_heap_buffer *buffer;
 	DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+	struct cc_shared_layout layout;
 	unsigned long size_remaining = len;
+	unsigned long sg_remaining = len;
 	unsigned int max_order = orders[0];
 	struct system_heap_priv *priv = dma_heap_get_drvdata(heap);
 	bool cc_shared = priv->cc_shared;
@@ -416,6 +409,16 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
 	struct page *page, *tmp_page;
 	int i, ret = -ENOMEM;
 
+	if (cc_shared) {
+		int err;
+
+		err = cc_shared_calc_layout(len, &layout);
+		if (err)
+			return ERR_PTR(err);
+
+		size_remaining = layout.shared_size;
+	}
+
 	buffer = kzalloc_obj(*buffer);
 	if (!buffer)
 		return ERR_PTR(-ENOMEM);
@@ -438,7 +441,8 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
 			goto free_buffer;
 		}
 
-		page = alloc_largest_available(size_remaining, max_order);
+		page = alloc_largest_available(size_remaining, max_order,
+					       cc_shared);
 		if (!page)
 			goto free_buffer;
 
@@ -454,19 +458,15 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
 
 	sg = table->sgl;
 	list_for_each_entry_safe(page, tmp_page, &pages, lru) {
-		sg_set_page(sg, page, page_size(page), 0);
+		unsigned long sg_len;
+
+		sg_len = min_t(unsigned long, page_size(page), sg_remaining);
+		sg_set_page(sg, page, sg_len, 0);
+		sg_remaining -= sg_len;
 		sg = sg_next(sg);
 		list_del(&page->lru);
 	}
 
-	if (cc_shared_buffer(buffer)) {
-		for_each_sgtable_sg(table, sg, i) {
-			ret = system_heap_set_page_decrypted(sg_page(sg));
-			if (ret)
-				goto free_pages;
-		}
-	}
-
 	/* create the dmabuf */
 	exp_info.exp_name = dma_heap_get_name(heap);
 	exp_info.ops = &system_heap_buf_ops;
@@ -481,22 +481,12 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
 	return dmabuf;
 
 free_pages:
-	for_each_sgtable_sg(table, sg, i) {
-		struct page *p = sg_page(sg);
-
-		/*
-		 * Intentionally leak pages that cannot be re-encrypted
-		 * to prevent shared memory from being reused.
-		 */
-		if (cc_shared_buffer(buffer) &&
-		    system_heap_set_page_encrypted(p))
-			continue;
-		__free_pages(p, compound_order(p));
-	}
+	for_each_sgtable_sg(table, sg, i)
+		system_heap_free_page(sg_page(sg), cc_shared);
 	sg_free_table(table);
 free_buffer:
 	list_for_each_entry_safe(page, tmp_page, &pages, lru)
-		__free_pages(page, compound_order(page));
+		system_heap_free_page(page, cc_shared);
 	kfree(buffer);
 
 	return ERR_PTR(ret);
diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c
index 1ab3581b096a4..3be779093f3c6 100644
--- a/drivers/hv/connection.c
+++ b/drivers/hv/connection.c
@@ -13,6 +13,7 @@
 #include <linux/sched.h>
 #include <linux/wait.h>
 #include <linux/delay.h>
+#include <linux/cc_platform.h>
 #include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/slab.h>
@@ -263,29 +264,27 @@ int vmbus_connect(void)
 		goto cleanup;
 	}
 
-	ret = set_memory_decrypted((unsigned long)
-				vmbus_connection.monitor_pages[0], 1);
-	ret |= set_memory_decrypted((unsigned long)
-				vmbus_connection.monitor_pages[1], 1);
-	if (ret) {
-		/*
-		 * If set_memory_decrypted() fails, the encryption state
-		 * of the memory is unknown. So leak the memory instead
-		 * of risking returning decrypted memory to the free list.
-		 * For simplicity, always handle both pages the same.
-		 */
-		vmbus_connection.monitor_pages[0] = NULL;
-		vmbus_connection.monitor_pages[1] = NULL;
-		goto cleanup;
+	if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) {
+		ret = set_memory_decrypted((unsigned long)vmbus_connection.monitor_pages[0],
+					   1);
+		ret |= set_memory_decrypted((unsigned long)vmbus_connection.monitor_pages[1],
+					    1);
+		if (ret) {
+			/*
+			 * If set_memory_decrypted() fails, the encryption state
+			 * of the memory is unknown. So leak the memory instead
+			 * of risking returning decrypted memory to the free list.
+			 * For simplicity, always handle both pages the same.
+			 */
+			vmbus_connection.monitor_pages[0] = NULL;
+			vmbus_connection.monitor_pages[1] = NULL;
+			goto cleanup;
+		}
+	} else {
+		memset(vmbus_connection.monitor_pages[0], 0, HV_HYP_PAGE_SIZE);
+		memset(vmbus_connection.monitor_pages[1], 0, HV_HYP_PAGE_SIZE);
 	}
 
-	/*
-	 * Set_memory_decrypted() will change the memory contents if
-	 * decryption occurs, so zero monitor pages here.
-	 */
-	memset(vmbus_connection.monitor_pages[0], 0x00, HV_HYP_PAGE_SIZE);
-	memset(vmbus_connection.monitor_pages[1], 0x00, HV_HYP_PAGE_SIZE);
-
 	msginfo = kzalloc(sizeof(*msginfo) +
 			  sizeof(struct vmbus_channel_initiate_contact),
 			  GFP_KERNEL);
diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c
index fe50090dcc011..6e836a02910a0 100644
--- a/drivers/hv/hv.c
+++ b/drivers/hv/hv.c
@@ -123,12 +123,13 @@ static int hv_alloc_page(void **page, bool decrypt, const char *note)
 	if (!*page)
 		return -ENOMEM;
 
-	if (decrypt)
+	if (decrypt) {
 		ret = set_memory_decrypted((unsigned long)*page, 1);
-	if (ret)
-		goto failed;
-
-	memset(*page, 0, PAGE_SIZE);
+		if (ret)
+			goto failed;
+	} else {
+		memset(*page, 0, PAGE_SIZE);
+	}
 	return 0;
 
 failed:
diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c
index 31256cb22b39e..af6b1531d6006 100644
--- a/drivers/hv/hv_common.c
+++ b/drivers/hv/hv_common.c
@@ -505,8 +505,6 @@ int hv_common_cpu_init(unsigned int cpu)
 				/* It may be unsafe to free 'mem' */
 				return ret;
 			}
-
-			memset(mem, 0x00, pgcount * HV_HYP_PAGE_SIZE);
 		}
 
 		/*
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 58c624513cd43..59baf2687612b 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -1627,7 +1627,7 @@ static void *iommu_dma_alloc_pages(struct device *dev, size_t size,
 	struct page *page = NULL;
 	void *cpu_addr;
 
-	page = dma_alloc_contiguous(dev, alloc_size, gfp);
+	page = dma_alloc_contiguous(dev, alloc_size, gfp, 0);
 	if (!page)
 		page = alloc_pages_node(node, gfp, get_order(alloc_size));
 	if (!page)
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index e9807af235373..a8f37fb978452 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -8,6 +8,7 @@
 #include <linux/acpi_iort.h>
 #include <linux/bitfield.h>
 #include <linux/bitmap.h>
+#include <linux/cc_shared.h>
 #include <linux/cpu.h>
 #include <linux/crash_dump.h>
 #include <linux/delay.h>
@@ -19,7 +20,6 @@
 #include <linux/irqdomain.h>
 #include <linux/list.h>
 #include <linux/log2.h>
-#include <linux/mem_encrypt.h>
 #include <linux/memblock.h>
 #include <linux/mm.h>
 #include <linux/msi.h>
@@ -29,9 +29,9 @@
 #include <linux/of_pci.h>
 #include <linux/of_platform.h>
 #include <linux/percpu.h>
-#include <linux/set_memory.h>
 #include <linux/slab.h>
 #include <linux/syscore_ops.h>
+#include <linux/topology.h>
 
 #include <linux/irqchip.h>
 #include <linux/irqchip/arm-gic-v3.h>
@@ -195,6 +195,7 @@ static DEFINE_PER_CPU(struct cpu_lpi_count, cpu_lpi_count);
 static LIST_HEAD(its_nodes);
 static DEFINE_RAW_SPINLOCK(its_lock);
 static struct rdists *gic_rdists;
+static unsigned int vpe_l1_prealloc_order = UINT_MAX;
 static struct irq_domain *its_parent;
 
 static unsigned long its_list_map;
@@ -213,41 +214,31 @@ static gfp_t gfp_flags_quirk;
 static struct page *its_alloc_pages_node(int node, gfp_t gfp,
 					 unsigned int order)
 {
-	struct page *page;
-	int ret = 0;
-
-	page = alloc_pages_node(node, gfp | gfp_flags_quirk, order);
-
-	if (!page)
-		return NULL;
+	struct cc_shared_pages mem;
 
-	ret = set_memory_decrypted((unsigned long)page_address(page),
-				   1 << order);
-	/*
-	 * If set_memory_decrypted() fails then we don't know what state the
-	 * page is in, so we can't free it. Instead we leak it.
-	 * set_memory_decrypted() will already have WARNed.
-	 */
-	if (ret)
+	if (alloc_cc_shared_pages_node(node, gfp | gfp_flags_quirk,
+				       PAGE_SIZE << order, &mem))
 		return NULL;
 
-	return page;
+	return mem.page;
 }
 
 static struct page *its_alloc_pages(gfp_t gfp, unsigned int order)
 {
-	return its_alloc_pages_node(NUMA_NO_NODE, gfp, order);
+	return its_alloc_pages_node(numa_mem_id(), gfp, order);
 }
 
 static void its_free_pages(void *addr, unsigned int order)
 {
-	/*
-	 * If the memory cannot be encrypted again then we must leak the pages.
-	 * set_memory_encrypted() will already have WARNed.
-	 */
-	if (set_memory_encrypted((unsigned long)addr, 1 << order))
+	struct cc_shared_layout layout;
+	struct cc_shared_pages mem;
+
+	if (WARN_ON(cc_shared_calc_layout(PAGE_SIZE << order, &layout)))
 		return;
-	free_pages((unsigned long)addr, order);
+
+	mem.page = virt_to_page(addr);
+	mem.shared_size = layout.shared_size;
+	free_cc_shared_pages(&mem);
 }
 
 static struct gen_pool *itt_pool;
@@ -272,7 +263,8 @@ static void *itt_alloc_pool(int node, int size)
 		if (!page)
 			break;
 
-		gen_pool_add(itt_pool, (unsigned long)page_address(page), PAGE_SIZE, node);
+		gen_pool_add(itt_pool, (unsigned long)page_address(page),
+			     cc_shared_granule_size(), node);
 	} while (!addr);
 
 	return (void *)addr;
@@ -2893,12 +2885,35 @@ static bool allocate_vpe_l2_table(int cpu, u32 id)
 	return true;
 }
 
-static int allocate_vpe_l1_table(void)
+static unsigned int vpe_l1_table_order(u64 val)
+{
+	unsigned int psz;
+	u64 npg;
+
+	switch (FIELD_GET(GICR_VPROPBASER_4_1_PAGE_SIZE, val)) {
+	default:
+	case GIC_PAGE_SIZE_4K:
+		psz = SZ_4K;
+		break;
+	case GIC_PAGE_SIZE_16K:
+		psz = SZ_16K;
+		break;
+	case GIC_PAGE_SIZE_64K:
+		psz = SZ_64K;
+		break;
+	}
+
+	npg = FIELD_GET(GICR_VPROPBASER_4_1_SIZE, val) + 1;
+	return get_order(npg * psz);
+}
+
+static int allocate_vpe_l1_table(bool use_prealloc)
 {
 	void __iomem *vlpi_base = gic_data_rdist_vlpi_base();
 	u64 val, gpsz, npg, pa;
 	unsigned int psz = SZ_64K;
 	unsigned int np, epp, esz;
+	unsigned int order;
 	struct page *page;
 
 	if (!gic_rdists->has_rvpeid)
@@ -2991,7 +3006,16 @@ static int allocate_vpe_l1_table(void)
 
 	pr_debug("np = %d, npg = %lld, psz = %d, epp = %d, esz = %d\n",
 		 np, npg, psz, epp, esz);
-	page = its_alloc_pages(GFP_ATOMIC | __GFP_ZERO, get_order(np * PAGE_SIZE));
+	order = get_order(np * PAGE_SIZE);
+	if (use_prealloc) {
+		if (WARN_ON_ONCE(order != vpe_l1_prealloc_order))
+			return -EINVAL;
+
+		page = gic_data_rdist()->vpe_l1_prealloc;
+		gic_data_rdist()->vpe_l1_prealloc = NULL;
+	} else {
+		page = its_alloc_pages(GFP_KERNEL | __GFP_ZERO, order);
+	}
 	if (!page)
 		return -ENOMEM;
 
@@ -3008,6 +3032,9 @@ static int allocate_vpe_l1_table(void)
 	val |= GICR_VPROPBASER_4_1_VALID;
 
 out:
+	if (!use_prealloc)
+		vpe_l1_prealloc_order = vpe_l1_table_order(val);
+
 	gicr_write_vpropbaser(val, vlpi_base + GICR_VPROPBASER);
 	cpumask_set_cpu(smp_processor_id(), gic_data_rdist()->vpe_table_mask);
 
@@ -3147,7 +3174,7 @@ static u64 its_clear_vpend_valid(void __iomem *vlpi_base, u64 clr, u64 set)
 	return val;
 }
 
-static void its_cpu_init_lpis(void)
+static void its_cpu_init_lpis(bool use_prealloc)
 {
 	void __iomem *rbase = gic_data_rdist_rd_base();
 	struct page *pend_page;
@@ -3260,7 +3287,7 @@ static void its_cpu_init_lpis(void)
 		val = its_clear_vpend_valid(vlpi_base, 0, 0);
 	}
 
-	if (allocate_vpe_l1_table()) {
+	if (allocate_vpe_l1_table(use_prealloc)) {
 		/*
 		 * If the allocation has failed, we're in massive trouble.
 		 * Disable direct injection, and pray that no VM was
@@ -5431,7 +5458,7 @@ static int redist_disable_lpis(void)
 	return 0;
 }
 
-int its_cpu_init(void)
+int its_cpu_init(bool use_prealloc)
 {
 	if (!list_empty(&its_nodes)) {
 		int ret;
@@ -5440,13 +5467,66 @@ int its_cpu_init(void)
 		if (ret)
 			return ret;
 
-		its_cpu_init_lpis();
+		its_cpu_init_lpis(use_prealloc);
 		its_cpu_init_collections();
 	}
 
 	return 0;
 }
 
+static int its_vpe_l1_prepare(unsigned int cpu)
+{
+	struct page **prealloc;
+
+	if (!gic_rdists->has_rvpeid ||
+	    vpe_l1_prealloc_order == UINT_MAX ||
+	    (gic_data_rdist_cpu(cpu)->flags & RD_LOCAL_LPI_ENABLED))
+		return 0;
+
+	prealloc = &gic_data_rdist_cpu(cpu)->vpe_l1_prealloc;
+	if (*prealloc)
+		return 0;
+
+	*prealloc = its_alloc_pages_node(cpu_to_node(cpu),
+					 GFP_KERNEL | __GFP_ZERO,
+					 vpe_l1_prealloc_order);
+	return *prealloc ? 0 : -ENOMEM;
+}
+
+static int its_vpe_l1_cleanup(unsigned int cpu)
+{
+	struct page *page;
+
+	page = xchg(&gic_data_rdist_cpu(cpu)->vpe_l1_prealloc, NULL);
+	if (page)
+		its_free_pages(page_address(page), vpe_l1_prealloc_order);
+
+	return 0;
+}
+
+static int __init its_vpe_l1_cpuhp_init(void)
+{
+	int prepare_state, state;
+
+	state = cpuhp_setup_state_nocalls(CPUHP_BP_PREPARE_DYN,
+					  "irqchip/arm/gicv3-vpe:prepare",
+					  its_vpe_l1_prepare,
+					  its_vpe_l1_cleanup);
+	if (state < 0)
+		return state;
+	prepare_state = state;
+
+	state = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
+					  "irqchip/arm/gicv3-vpe:online",
+					  its_vpe_l1_cleanup, NULL);
+	if (state < 0) {
+		cpuhp_remove_state_nocalls(prepare_state);
+		return state;
+	}
+
+	return 0;
+}
+
 static void rdist_memreserve_cpuhp_cleanup_workfn(struct work_struct *work)
 {
 	cpuhp_remove_state_nocalls(gic_rdists->cpuhp_memreserve_state);
@@ -5871,6 +5951,15 @@ int __init its_init(struct fwnode_handle *handle, struct rdists *rdists,
 	if (WARN_ON(!has_v4_1 && rdists->has_rvpeid))
 		rdists->has_rvpeid = false;
 
+	if (rdists->has_rvpeid) {
+		err = its_vpe_l1_cpuhp_init();
+		if (err) {
+			rdists->has_rvpeid = false;
+			rdists->has_vlpis = false;
+			pr_err("ITS: Failed to prepare VPE tables, disabling GICv4 support\n");
+		}
+	}
+
 	if (has_v4 & rdists->has_vlpis) {
 		const struct irq_domain_ops *sgi_ops;
 
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 6e1fa5b247fc4..e2e00372dcfb0 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -1320,7 +1320,7 @@ static int gic_starting_cpu(unsigned int cpu)
 	gic_cpu_init();
 
 	if (gic_dist_supports_lpis())
-		its_cpu_init();
+		its_cpu_init(true);
 
 	return 0;
 }
@@ -2053,7 +2053,7 @@ static int __init gic_init_bases(phys_addr_t dist_phys_base,
 
 	if (gic_dist_supports_lpis()) {
 		its_init(handle, &gic_data.rdists, gic_data.domain, dist_prio_irq);
-		its_cpu_init();
+		its_cpu_init(false);
 		its_lpi_memreserve_init();
 	} else {
 		if (IS_ENABLED(CONFIG_ARM_GIC_V2M))
diff --git a/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c b/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c
index 26fe9c3f22e36..7d922e0a28f96 100644
--- a/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c
+++ b/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c
@@ -13,6 +13,7 @@
 #include <linux/mem_encrypt.h>
 #include <linux/mm.h>
 #include <linux/pgtable.h>
+#include <linux/string.h>
 
 #include <asm/hypervisor.h>
 
@@ -61,6 +62,8 @@ static int pkvm_set_memory_encrypted(unsigned long addr, int numpages)
 
 static int pkvm_set_memory_decrypted(unsigned long addr, int numpages)
 {
+	memset((void *)addr, 0, (size_t)numpages << PAGE_SHIFT);
+
 	return __set_memory_range(ARM_SMCCC_VENDOR_HYP_KVM_MEM_SHARE_FUNC_ID,
 				  addr, numpages);
 }
diff --git a/include/linux/cc_shared.h b/include/linux/cc_shared.h
new file mode 100644
index 0000000000000..5f8db7c468c5c
--- /dev/null
+++ b/include/linux/cc_shared.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_CC_SHARED_H
+#define _LINUX_CC_SHARED_H
+
+#include <linux/gfp_types.h>
+#include <linux/types.h>
+
+struct page;
+
+struct cc_shared_pages {
+	struct page *page;
+	size_t shared_size;
+};
+
+struct cc_shared_layout {
+	size_t requested_size;
+	size_t shared_size;
+	size_t alignment;
+};
+
+/*
+ * Architectures may override this to return the granule used for transitions
+ * between private and shared memory. The value must be a power of two and no
+ * smaller than PAGE_SIZE.
+ */
+size_t arch_cc_shared_granule_size(void);
+
+size_t cc_shared_granule_size(void);
+int cc_shared_calc_layout(size_t requested, struct cc_shared_layout *layout);
+bool cc_shared_range_valid(phys_addr_t base, size_t size);
+int cc_make_shared(void *addr, size_t size);
+int cc_make_private(void *addr, size_t size);
+int alloc_cc_shared_pages_node(int nid, gfp_t gfp,
+		size_t requested, struct cc_shared_pages *mem);
+int alloc_cc_shared_pages(gfp_t gfp,
+		size_t requested, struct cc_shared_pages *mem);
+void free_cc_shared_pages(struct cc_shared_pages *mem);
+
+#endif /* _LINUX_CC_SHARED_H */
diff --git a/include/linux/dma-map-ops.h b/include/linux/dma-map-ops.h
index 8fae2b7deb20d..f3490dd3f8ace 100644
--- a/include/linux/dma-map-ops.h
+++ b/include/linux/dma-map-ops.h
@@ -99,10 +99,11 @@ int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base,
 		phys_addr_t limit, struct cma **res_cma, bool fixed);
 
 struct page *dma_alloc_from_contiguous(struct device *dev, size_t count,
-				       unsigned int order, bool no_warn);
+		unsigned int order, unsigned long attrs, bool no_warn);
 bool dma_release_from_contiguous(struct device *dev, struct page *pages,
 				 int count);
-struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp);
+struct page *dma_alloc_contiguous(struct device *dev, size_t size,
+				  gfp_t gfp, unsigned long attrs);
 void dma_free_contiguous(struct device *dev, struct page *page, size_t size);
 
 void dma_contiguous_early_fixup(phys_addr_t base, unsigned long size);
@@ -125,7 +126,7 @@ static inline int dma_contiguous_reserve_area(phys_addr_t size,
 	return -ENOSYS;
 }
 static inline struct page *dma_alloc_from_contiguous(struct device *dev,
-		size_t count, unsigned int order, bool no_warn)
+		size_t count, unsigned int order, unsigned long attrs, bool no_warn)
 {
 	return NULL;
 }
@@ -136,7 +137,7 @@ static inline bool dma_release_from_contiguous(struct device *dev,
 }
 /* Use fallback alloc() and free() when CONFIG_DMA_CMA=n */
 static inline struct page *dma_alloc_contiguous(struct device *dev, size_t size,
-		gfp_t gfp)
+		gfp_t gfp, unsigned long attrs)
 {
 	return NULL;
 }
diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
index ea5fd2374ebe0..a5565a7451009 100644
--- a/include/linux/irqchip/arm-gic-v3.h
+++ b/include/linux/irqchip/arm-gic-v3.h
@@ -621,6 +621,7 @@ struct rdists {
 		u64             flags;
 		cpumask_t	*vpe_table_mask;
 		void		*vpe_l1_base;
+		struct page	*vpe_l1_prealloc;
 	} __percpu		*rdist;
 	phys_addr_t		prop_table_pa;
 	void			*prop_table_va;
@@ -637,7 +638,7 @@ struct rdists {
 struct irq_domain;
 struct fwnode_handle;
 int __init its_lpi_memreserve_init(void);
-int its_cpu_init(void);
+int its_cpu_init(bool use_prealloc);
 int its_init(struct fwnode_handle *handle, struct rdists *rdists,
 	     struct irq_domain *domain, u8 irq_prio);
 int mbi_init(struct fwnode_handle *fwnode, struct irq_domain *parent);
diff --git a/kernel/dma/contiguous.c b/kernel/dma/contiguous.c
index 66093460584e8..1a29565c91858 100644
--- a/kernel/dma/contiguous.c
+++ b/kernel/dma/contiguous.c
@@ -39,6 +39,7 @@
 
 #include <asm/page.h>
 
+#include <linux/cc_shared.h>
 #include <linux/memblock.h>
 #include <linux/err.h>
 #include <linux/sizes.h>
@@ -357,19 +358,29 @@ int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base,
  * dma_alloc_from_contiguous() - allocate pages from contiguous area
  * @dev:   Pointer to device for which the allocation is performed.
  * @count: Requested number of pages.
- * @align: Requested alignment of pages (in PAGE_SIZE order).
+ * @align: Preferred alignment of pages (in PAGE_SIZE order).
+ * @attrs: DMA allocation attributes.
  * @no_warn: Avoid printing message about failed allocation.
  *
  * This function allocates memory buffer for specified device. It uses
  * device specific contiguous memory area if available or the default
  * global one. Requires architecture specific dev_get_cma_area() helper
  * function.
+ *
+ * The preferred alignment is capped at CONFIG_CMA_ALIGNMENT. The internal
+ * shared-allocation attribute requires at least the architecture shared
+ * granule alignment and fails if that exceeds the CMA alignment limit.
  */
 struct page *dma_alloc_from_contiguous(struct device *dev, size_t count,
-				       unsigned int align, bool no_warn)
+		unsigned int align, unsigned long attrs, bool no_warn)
 {
-	if (align > CONFIG_CMA_ALIGNMENT)
-		align = CONFIG_CMA_ALIGNMENT;
+	unsigned int required_align = 0;
+
+	if (attrs & __DMA_ATTR_ALLOC_CC_SHARED)
+		required_align = get_order(cc_shared_granule_size());
+	if (required_align > CONFIG_CMA_ALIGNMENT)
+		return NULL;
+	align = min(max(align, required_align), CONFIG_CMA_ALIGNMENT);
 
 	return cma_alloc(dev_get_cma_area(dev), count, align, no_warn);
 }
@@ -390,10 +401,9 @@ bool dma_release_from_contiguous(struct device *dev, struct page *pages,
 	return cma_release(dev_get_cma_area(dev), pages, count);
 }
 
-static struct page *cma_alloc_aligned(struct cma *cma, size_t size, gfp_t gfp)
+static struct page *cma_alloc_aligned(struct cma *cma, size_t size, gfp_t gfp,
+				      unsigned int align)
 {
-	unsigned int align = min(get_order(size), CONFIG_CMA_ALIGNMENT);
-
 	return cma_alloc(cma, size >> PAGE_SHIFT, align, gfp & __GFP_NOWARN);
 }
 
@@ -402,6 +412,7 @@ static struct page *cma_alloc_aligned(struct cma *cma, size_t size, gfp_t gfp)
  * @dev:   Pointer to device for which the allocation is performed.
  * @size:  Requested allocation size.
  * @gfp:   Allocation flags.
+ * @attrs: DMA allocation attributes.
  *
  * tries to use device specific contiguous memory area if available, or it
  * tries to use per-numa cma, if the allocation fails, it will fallback to
@@ -412,8 +423,11 @@ static struct page *cma_alloc_aligned(struct cma *cma, size_t size, gfp_t gfp)
  * there is no need to waste CMA pages for that kind; it also helps reduce
  * fragmentations.
  */
-struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp)
+struct page *dma_alloc_contiguous(struct device *dev, size_t size,
+				  gfp_t gfp, unsigned long attrs)
 {
+	unsigned int required_align = 0;
+	unsigned int align = get_order(size);
 #ifdef CONFIG_DMA_NUMA_CMA
 	int nid = dev_to_node(dev);
 #endif
@@ -421,8 +435,13 @@ struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp)
 	/* CMA can be used only in the context which permits sleeping */
 	if (!gfpflags_allow_blocking(gfp))
 		return NULL;
+	if (attrs & __DMA_ATTR_ALLOC_CC_SHARED)
+		required_align = get_order(cc_shared_granule_size());
+	if (required_align > CONFIG_CMA_ALIGNMENT)
+		return NULL;
+	align = min(max(align, required_align), CONFIG_CMA_ALIGNMENT);
 	if (dev->cma_area)
-		return cma_alloc_aligned(dev->cma_area, size, gfp);
+		return cma_alloc_aligned(dev->cma_area, size, gfp, align);
 	if (size <= PAGE_SIZE)
 		return NULL;
 
@@ -431,7 +450,7 @@ struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp)
 		struct cma *cma = dma_contiguous_numa_area[nid];
 		struct page *page;
 		if (cma) {
-			page = cma_alloc_aligned(cma, size, gfp);
+			page = cma_alloc_aligned(cma, size, gfp, align);
 			if (page)
 				return page;
 		}
@@ -440,7 +459,7 @@ struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp)
 	if (!dma_contiguous_default_area)
 		return NULL;
 
-	return cma_alloc_aligned(dma_contiguous_default_area, size, gfp);
+	return cma_alloc_aligned(dma_contiguous_default_area, size, gfp, align);
 }
 
 /**
diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
index da665ca22d5c0..09a8a842fc6fc 100644
--- a/kernel/dma/direct.c
+++ b/kernel/dma/direct.c
@@ -11,10 +11,10 @@
 #include <linux/scatterlist.h>
 #include <linux/pfn.h>
 #include <linux/vmalloc.h>
-#include <linux/set_memory.h>
 #include <linux/slab.h>
 #include <linux/pci-p2pdma.h>
 #include <linux/cc_platform.h>
+#include <linux/cc_shared.h>
 
 #include "direct.h"
 
@@ -85,7 +85,7 @@ static int dma_set_decrypted(struct device *dev, void *vaddr, size_t size)
 {
 	int ret;
 
-	ret = set_memory_decrypted((unsigned long)vaddr, PFN_UP(size));
+	ret = cc_make_shared(vaddr, size);
 	if (ret)
 		pr_warn_ratelimited("leaking DMA memory that can't be decrypted\n");
 	return ret;
@@ -95,7 +95,7 @@ static int dma_set_encrypted(struct device *dev, void *vaddr, size_t size)
 {
 	int ret;
 
-	ret = set_memory_encrypted((unsigned long)vaddr, PFN_UP(size));
+	ret = cc_make_private(vaddr, size);
 	if (ret)
 		pr_warn_ratelimited("leaking DMA memory that can't be re-encrypted\n");
 	return ret;
@@ -115,7 +115,7 @@ static struct page *dma_direct_alloc_swiotlb(struct device *dev, size_t size,
 }
 
 static struct page *__dma_direct_alloc_pages(struct device *dev, size_t size,
-		gfp_t gfp, bool allow_highmem)
+		gfp_t gfp, bool allow_highmem, unsigned long attrs)
 {
 	int node = dev_to_node(dev);
 	struct page *page;
@@ -124,7 +124,7 @@ static struct page *__dma_direct_alloc_pages(struct device *dev, size_t size,
 	WARN_ON_ONCE(!PAGE_ALIGNED(size));
 
 	gfp |= dma_direct_optimal_gfp_mask(dev, &phys_limit);
-	page = dma_alloc_contiguous(dev, size, gfp);
+	page = dma_alloc_contiguous(dev, size, gfp, attrs);
 	if (page) {
 		if (dma_coherent_ok(dev, page_to_phys(page), size) &&
 		    (allow_highmem || !PageHighMem(page)))
@@ -184,7 +184,7 @@ static void *dma_direct_alloc_no_mapping(struct device *dev, size_t size,
 {
 	struct page *page;
 
-	page = __dma_direct_alloc_pages(dev, size, gfp & ~__GFP_ZERO, true);
+	page = __dma_direct_alloc_pages(dev, size, gfp & ~__GFP_ZERO, true, 0);
 	if (!page)
 		return NULL;
 
@@ -205,13 +205,15 @@ void *dma_direct_alloc(struct device *dev, size_t size,
 	bool remap = false, set_uncached = false;
 	bool mark_mem_decrypt = false;
 	bool allow_highmem = true;
+	struct cc_shared_layout layout;
 	struct page *page;
 	void *cpu_addr;
 
 	if (force_dma_unencrypted(dev))
 		attrs |= __DMA_ATTR_ALLOC_CC_SHARED;
 
-	if (attrs & __DMA_ATTR_ALLOC_CC_SHARED) {
+	mark_mem_decrypt = attrs & __DMA_ATTR_ALLOC_CC_SHARED;
+	if (mark_mem_decrypt) {
 		/*
 		 * Unencrypted/shared DMA requires a linear-mapped buffer
 		 * address to look up the PFN and set architecture-required PFN
@@ -219,7 +221,6 @@ void *dma_direct_alloc(struct device *dev, size_t size,
 		 * allocation.
 		 */
 		allow_highmem = false;
-		mark_mem_decrypt = true;
 	}
 
 	size = PAGE_ALIGN(size);
@@ -285,8 +286,15 @@ void *dma_direct_alloc(struct device *dev, size_t size,
 		return NULL;
 	}
 
+	if (mark_mem_decrypt) {
+		if (cc_shared_calc_layout(size, &layout))
+			return NULL;
+		size = layout.shared_size;
+	}
+
 	/* we always manually zero the memory once we are done */
-	page = __dma_direct_alloc_pages(dev, size, gfp & ~__GFP_ZERO, allow_highmem);
+	page = __dma_direct_alloc_pages(dev, size, gfp & ~__GFP_ZERO,
+					allow_highmem, attrs);
 	if (!page)
 		return NULL;
 
@@ -305,7 +313,7 @@ void *dma_direct_alloc(struct device *dev, size_t size,
 		void *lm_addr;
 
 		lm_addr = page_address(page);
-		if (set_memory_decrypted((unsigned long)lm_addr, PFN_UP(size)))
+		if (dma_set_decrypted(dev, lm_addr, size))
 			goto out_leak_pages;
 	}
 
@@ -324,7 +332,9 @@ void *dma_direct_alloc(struct device *dev, size_t size,
 		cpu_addr = page_address(page);
 	}
 
-	memset(cpu_addr, 0, size);
+	/* Zero after remapping because the page may be in HighMem. */
+	if (!mark_mem_decrypt)
+		memset(cpu_addr, 0, size);
 
 	if (set_uncached) {
 		void *uncached_cpu_addr;
@@ -362,6 +372,7 @@ void dma_direct_free(struct device *dev, size_t size,
 	phys_addr_t phys;
 	bool mark_mem_encrypted = false;
 	struct io_tlb_pool *swiotlb_pool;
+	struct cc_shared_layout layout;
 	unsigned int page_order = get_order(size);
 
 	/*
@@ -406,6 +417,12 @@ void dma_direct_free(struct device *dev, size_t size,
 		/* Swiotlb doesn't need a page attribute update on free */
 		mark_mem_encrypted = false;
 
+	if (mark_mem_encrypted) {
+		if (WARN_ON_ONCE(cc_shared_calc_layout(size, &layout)))
+			return;
+		size = layout.shared_size;
+	}
+
 	if (is_vmalloc_addr(cpu_addr)) {
 		vunmap(cpu_addr);
 	} else {
@@ -417,10 +434,8 @@ void dma_direct_free(struct device *dev, size_t size,
 		void *lm_addr;
 
 		lm_addr = phys_to_virt(phys);
-		if (set_memory_encrypted((unsigned long)lm_addr, PFN_UP(size))) {
-			pr_warn_ratelimited("leaking DMA memory that can't be re-encrypted\n");
+		if (dma_set_encrypted(dev, lm_addr, size))
 			return;
-		}
 	}
 
 	if (swiotlb_pool)
@@ -433,12 +448,16 @@ struct page *dma_direct_alloc_pages(struct device *dev, size_t size,
 		dma_addr_t *dma_handle, enum dma_data_direction dir, gfp_t gfp)
 {
 	unsigned long attrs = 0;
+	struct cc_shared_layout layout;
 	struct page *page;
 	void *cpu_addr;
+	bool mark_mem_decrypt;
 
 	if (force_dma_unencrypted(dev))
 		attrs |= __DMA_ATTR_ALLOC_CC_SHARED;
 
+	mark_mem_decrypt = attrs & __DMA_ATTR_ALLOC_CC_SHARED;
+
 	if ((attrs & __DMA_ATTR_ALLOC_CC_SHARED) && dma_direct_use_pool(dev, gfp))
 		return dma_direct_alloc_from_pool(dev, size, dma_handle,
 						  &cpu_addr, gfp, attrs);
@@ -449,19 +468,28 @@ struct page *dma_direct_alloc_pages(struct device *dev, size_t size,
 			return NULL;
 
 		cpu_addr = page_address(page);
+		mark_mem_decrypt = false;
 		goto setup_page;
 	}
 
-	page = __dma_direct_alloc_pages(dev, size, gfp, false);
+	if (mark_mem_decrypt) {
+		if (cc_shared_calc_layout(size, &layout))
+			return NULL;
+		size = layout.shared_size;
+	}
+
+	page = __dma_direct_alloc_pages(dev, size, gfp, false, attrs);
 	if (!page)
 		return NULL;
 
 	cpu_addr = page_address(page);
-	if ((attrs & __DMA_ATTR_ALLOC_CC_SHARED) &&
-	    dma_set_decrypted(dev, cpu_addr, size))
-		goto out_leak_pages;
 setup_page:
-	memset(cpu_addr, 0, size);
+	if (mark_mem_decrypt) {
+		if (dma_set_decrypted(dev, cpu_addr, size))
+			goto out_leak_pages;
+	} else {
+		memset(cpu_addr, 0, size);
+	}
 	*dma_handle = phys_to_dma_direct(dev, page_to_phys(page),
 					 attrs & __DMA_ATTR_ALLOC_CC_SHARED);
 	return page;
@@ -476,6 +504,7 @@ void dma_direct_free_pages(struct device *dev, size_t size,
 	phys_addr_t phys;
 	void *vaddr = page_address(page);
 	struct io_tlb_pool *swiotlb_pool;
+	struct cc_shared_layout layout;
 	/*
 	 * if the device had requested for an unencrypted buffer,
 	 * convert it to encrypted on free
@@ -492,6 +521,12 @@ void dma_direct_free_pages(struct device *dev, size_t size,
 	if (swiotlb_pool)
 		mark_mem_encrypted = false;
 
+	if (mark_mem_encrypted) {
+		if (WARN_ON_ONCE(cc_shared_calc_layout(size, &layout)))
+			return;
+		size = layout.shared_size;
+	}
+
 	if (mark_mem_encrypted && dma_set_encrypted(dev, vaddr, size))
 		return;
 
diff --git a/kernel/dma/ops_helpers.c b/kernel/dma/ops_helpers.c
index 6b5f9208d31c0..8320cc0fada50 100644
--- a/kernel/dma/ops_helpers.c
+++ b/kernel/dma/ops_helpers.c
@@ -66,7 +66,7 @@ struct page *dma_common_alloc_pages(struct device *dev, size_t size,
 	struct page *page;
 	phys_addr_t phys;
 
-	page = dma_alloc_contiguous(dev, size, gfp);
+	page = dma_alloc_contiguous(dev, size, gfp, 0);
 	if (!page)
 		page = alloc_pages_node(dev_to_node(dev), gfp, get_order(size));
 	if (!page)
diff --git a/kernel/dma/pool.c b/kernel/dma/pool.c
index 00f422a1e8966..f059312c15aa5 100644
--- a/kernel/dma/pool.c
+++ b/kernel/dma/pool.c
@@ -4,12 +4,12 @@
  * Copyright (C) 2020 Google LLC
  */
 #include <linux/cma.h>
+#include <linux/cc_shared.h>
 #include <linux/debugfs.h>
 #include <linux/dma-map-ops.h>
 #include <linux/dma-direct.h>
 #include <linux/init.h>
 #include <linux/genalloc.h>
-#include <linux/set_memory.h>
 #include <linux/slab.h>
 #include <linux/workqueue.h>
 #include <linux/cc_platform.h>
@@ -85,6 +85,9 @@ static bool cma_in_zone(gfp_t gfp)
 static int atomic_pool_expand(struct dma_gen_pool *dma_pool, size_t pool_size,
 			      gfp_t gfp)
 {
+	struct cc_shared_layout layout;
+	unsigned long attrs = 0;
+	unsigned int min_order = 0;
 	unsigned int order;
 	struct page *page = NULL;
 	bool leak_pages = false;
@@ -92,6 +95,17 @@ static int atomic_pool_expand(struct dma_gen_pool *dma_pool, size_t pool_size,
 	int ret = -ENOMEM;
 	pgprot_t prot __maybe_unused;
 
+	if (dma_pool->cc_shared) {
+		ret = cc_shared_calc_layout(pool_size, &layout);
+		if (ret)
+			goto out;
+		pool_size = layout.shared_size;
+		min_order = get_order(layout.alignment);
+		if (min_order > MAX_PAGE_ORDER)
+			return -E2BIG;
+		attrs = __DMA_ATTR_ALLOC_CC_SHARED;
+	}
+
 	/* Cannot allocate larger than MAX_PAGE_ORDER */
 	order = min(get_order(pool_size), MAX_PAGE_ORDER);
 
@@ -99,10 +113,10 @@ static int atomic_pool_expand(struct dma_gen_pool *dma_pool, size_t pool_size,
 		pool_size = 1 << (PAGE_SHIFT + order);
 		if (cma_in_zone(gfp))
 			page = dma_alloc_from_contiguous(NULL, 1 << order,
-							 order, false);
+							 order, attrs, false);
 		if (!page)
 			page = alloc_pages(gfp | __GFP_NOWARN, order);
-	} while (!page && order-- > 0);
+	} while (!page && order-- > min_order);
 	if (!page)
 		goto out;
 
@@ -126,8 +140,7 @@ static int atomic_pool_expand(struct dma_gen_pool *dma_pool, size_t pool_size,
 	 * shrink so no re-encryption occurs in dma_direct_free().
 	 */
 	if (dma_pool->cc_shared) {
-		ret = set_memory_decrypted((unsigned long)page_to_virt(page),
-					   1 << order);
+		ret = cc_make_shared(page_to_virt(page), pool_size);
 		if (ret) {
 			leak_pages = true;
 			goto remove_mapping;
@@ -144,7 +157,7 @@ static int atomic_pool_expand(struct dma_gen_pool *dma_pool, size_t pool_size,
 
 encrypt_mapping:
 	if (dma_pool->cc_shared &&
-	    set_memory_encrypted((unsigned long)page_to_virt(page), 1 << order))
+	    cc_make_private(page_to_virt(page), pool_size))
 		leak_pages = true;
 
 remove_mapping:
diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
index ded7016a46a71..8d71f2e0fd855 100644
--- a/kernel/dma/swiotlb.c
+++ b/kernel/dma/swiotlb.c
@@ -22,6 +22,7 @@
 
 #include <linux/cache.h>
 #include <linux/cc_platform.h>
+#include <linux/cc_shared.h>
 #include <linux/ctype.h>
 #include <linux/debugfs.h>
 #include <linux/dma-direct.h>
@@ -38,7 +39,6 @@
 #include <linux/pfn.h>
 #include <linux/rculist.h>
 #include <linux/scatterlist.h>
-#include <linux/set_memory.h>
 #include <linux/spinlock.h>
 #include <linux/string.h>
 #include <linux/swiotlb.h>
@@ -330,6 +330,14 @@ static inline unsigned long nr_slots(u64 val)
 	return DIV_ROUND_UP(val, IO_TLB_SIZE);
 }
 
+static unsigned long swiotlb_align_nslabs(unsigned long nslabs)
+{
+	unsigned long granule_nslabs;
+
+	granule_nslabs = cc_shared_granule_size() >> IO_TLB_SHIFT;
+	return ALIGN(nslabs, granule_nslabs);
+}
+
 static void swiotlb_mark_pool_used(struct io_tlb_pool *pool)
 {
 	unsigned long i;
@@ -369,13 +377,13 @@ void __init swiotlb_update_mem_attributes(void)
 
 	if (!mem->nslabs || mem->late_alloc)
 		return;
-	bytes = PAGE_ALIGN(mem->nslabs << IO_TLB_SHIFT);
+
+	bytes = ALIGN(mem->nslabs << IO_TLB_SHIFT, cc_shared_granule_size());
 
 	if (io_tlb_default_mem.cc_shared) {
 		int ret;
 
-		ret = set_memory_decrypted((unsigned long)mem->vaddr,
-					   bytes >> PAGE_SHIFT);
+		ret = cc_make_shared(mem->vaddr, bytes);
 		if (ret) {
 			pr_warn("Failed to decrypt default memory pool, disabling it\n");
 			swiotlb_mark_pool_used(mem);
@@ -433,10 +441,11 @@ static void add_mem_pool(struct io_tlb_mem *mem, struct io_tlb_pool *pool)
 }
 
 static void __init *swiotlb_memblock_alloc(unsigned long nslabs,
-		unsigned int flags,
+		unsigned long *alloc_nslabs, unsigned int flags,
 		int (*remap)(void *tlb, unsigned long nslabs))
 {
-	size_t bytes = PAGE_ALIGN(nslabs << IO_TLB_SHIFT);
+	unsigned long aligned_nslabs = swiotlb_align_nslabs(nslabs);
+	size_t bytes = aligned_nslabs << IO_TLB_SHIFT;
 	void *tlb;
 
 	/*
@@ -445,9 +454,9 @@ static void __init *swiotlb_memblock_alloc(unsigned long nslabs,
 	 * memory encryption.
 	 */
 	if (flags & SWIOTLB_ANY)
-		tlb = memblock_alloc(bytes, PAGE_SIZE);
+		tlb = memblock_alloc(bytes, cc_shared_granule_size());
 	else
-		tlb = memblock_alloc_low(bytes, PAGE_SIZE);
+		tlb = memblock_alloc_low(bytes, cc_shared_granule_size());
 
 	if (!tlb) {
 		pr_warn("%s: Failed to allocate %zu bytes tlb structure\n",
@@ -455,12 +464,13 @@ static void __init *swiotlb_memblock_alloc(unsigned long nslabs,
 		return NULL;
 	}
 
-	if (remap && remap(tlb, nslabs) < 0) {
-		memblock_free(tlb, PAGE_ALIGN(bytes));
+	if (remap && remap(tlb, aligned_nslabs) < 0) {
+		memblock_free(tlb, bytes);
 		pr_warn("%s: Failed to remap %zu bytes\n", __func__, bytes);
 		return NULL;
 	}
 
+	*alloc_nslabs = aligned_nslabs;
 	return tlb;
 }
 
@@ -473,6 +483,7 @@ void __init swiotlb_init_remap(bool addressing_limit, unsigned int flags,
 {
 	struct io_tlb_pool *mem = &io_tlb_default_mem.defpool;
 	unsigned long nslabs;
+	unsigned long alloc_nslabs;
 	unsigned int nareas;
 	size_t alloc_size;
 	void *tlb;
@@ -497,13 +508,14 @@ void __init swiotlb_init_remap(bool addressing_limit, unsigned int flags,
 		swiotlb_adjust_nareas(num_possible_cpus());
 
 	nslabs = default_nslabs;
-	nareas = limit_nareas(default_nareas, nslabs);
-	while ((tlb = swiotlb_memblock_alloc(nslabs, flags, remap)) == NULL) {
+	while ((tlb = swiotlb_memblock_alloc(nslabs, &alloc_nslabs, flags,
+					     remap)) == NULL) {
 		if (nslabs <= IO_TLB_MIN_SLABS)
 			return;
 		nslabs = ALIGN(nslabs >> 1, IO_TLB_SEGSIZE);
-		nareas = limit_nareas(nareas, nslabs);
 	}
+	nslabs = alloc_nslabs;
+	nareas = limit_nareas(default_nareas, nslabs);
 
 	if (default_nslabs != nslabs) {
 		pr_info("SWIOTLB bounce buffer size adjusted %lu -> %lu slabs",
@@ -578,7 +590,7 @@ int swiotlb_init_late(size_t size, gfp_t gfp_mask,
 		swiotlb_adjust_nareas(num_possible_cpus());
 
 retry:
-	order = get_order(nslabs << IO_TLB_SHIFT);
+	order = get_order(ALIGN(nslabs << IO_TLB_SHIFT, cc_shared_granule_size()));
 	nslabs = SLABS_PER_PAGE << order;
 
 	while ((SLABS_PER_PAGE << order) > IO_TLB_MIN_SLABS) {
@@ -587,6 +599,8 @@ int swiotlb_init_late(size_t size, gfp_t gfp_mask,
 		if (vstart)
 			break;
 		order--;
+		if (order < get_order(cc_shared_granule_size()))
+			break;
 		nslabs = SLABS_PER_PAGE << order;
 		retried = true;
 	}
@@ -626,8 +640,7 @@ int swiotlb_init_late(size_t size, gfp_t gfp_mask,
 		goto error_slots;
 
 	if (io_tlb_default_mem.cc_shared) {
-		rc = set_memory_decrypted((unsigned long)vstart,
-					  (nslabs << IO_TLB_SHIFT) >> PAGE_SHIFT);
+		rc = cc_make_shared(vstart, nslabs << IO_TLB_SHIFT);
 		if (rc) {
 			leak_pages = true;
 			goto error_decrypt;
@@ -667,11 +680,11 @@ void __init swiotlb_exit(void)
 
 	pr_info("tearing down default memory pool\n");
 	tbl_vaddr = (unsigned long)phys_to_virt(mem->start);
-	tbl_size = PAGE_ALIGN(mem->end - mem->start);
+	tbl_size = ALIGN(mem->end - mem->start, cc_shared_granule_size());
 	slots_size = PAGE_ALIGN(array_size(sizeof(*mem->slots), mem->nslabs));
 
 	if (io_tlb_default_mem.cc_shared) {
-		if (set_memory_encrypted(tbl_vaddr, tbl_size >> PAGE_SHIFT))
+		if (cc_make_private((void *)tbl_vaddr, tbl_size))
 			leak_pages = true;
 	}
 
@@ -711,12 +724,15 @@ void __init swiotlb_exit(void)
 static struct page *alloc_dma_pages(gfp_t gfp, size_t bytes,
 		u64 phys_limit, unsigned long attrs)
 {
-	unsigned int order = get_order(bytes);
 	bool cc_shared = attrs & __DMA_ATTR_ALLOC_CC_SHARED;
+	unsigned int order;
 	struct page *page;
 	phys_addr_t paddr;
 	void *vaddr;
 
+	if (cc_shared)
+		bytes = ALIGN(bytes, cc_shared_granule_size());
+	order = get_order(bytes);
 	page = alloc_pages(gfp, order);
 	if (!page)
 		return NULL;
@@ -728,13 +744,13 @@ static struct page *alloc_dma_pages(gfp_t gfp, size_t bytes,
 	}
 
 	vaddr = phys_to_virt(paddr);
-	if (cc_shared && set_memory_decrypted((unsigned long)vaddr, PFN_UP(bytes)))
+	if (cc_shared && cc_make_shared(vaddr, bytes))
 		goto error;
 	return page;
 
 error:
 	/* Intentional leak if pages cannot be encrypted again. */
-	if (cc_shared && !set_memory_encrypted((unsigned long)vaddr, PFN_UP(bytes)))
+	if (cc_shared && !cc_make_private(vaddr, bytes))
 		__free_pages(page, order);
 	return NULL;
 }
@@ -807,9 +823,11 @@ static void swiotlb_free_tlb(void *vaddr, size_t bytes, bool cc_shared)
 	    dma_free_from_pool(NULL, vaddr, bytes))
 		return;
 
+	if (cc_shared)
+		bytes = ALIGN(bytes, cc_shared_granule_size());
 	/* Intentional leak if pages cannot be encrypted again. */
 	if (!cc_shared ||
-	    !set_memory_encrypted((unsigned long)vaddr, PFN_UP(bytes)))
+	    !cc_make_private(vaddr, bytes))
 		__free_pages(virt_to_page(vaddr), get_order(bytes));
 }
 
@@ -863,6 +881,12 @@ static struct io_tlb_pool *swiotlb_alloc_pool(struct device *dev,
 		tlb_size = nslabs << IO_TLB_SHIFT;
 	}
 
+	/* Transient pools are tied to one mapping and cannot reuse padding. */
+	if (mem->cc_shared && !dev) {
+		nslabs = swiotlb_align_nslabs(nslabs);
+		tlb_size = nslabs << IO_TLB_SHIFT;
+	}
+
 	slot_order = get_order(array_size(sizeof(*pool->slots), nslabs));
 	pool->slots = (struct io_tlb_slot *)
 		__get_free_pages(gfp, slot_order);
@@ -2002,6 +2026,14 @@ static int rmem_swiotlb_device_init(struct reserved_mem *rmem,
 		return -EINVAL;
 	}
 
+	if (cc_platform_has(CC_ATTR_MEM_ENCRYPT) &&
+	    !cc_shared_range_valid(rmem->base, rmem->size)) {
+		dev_err(dev,
+			"Restricted DMA pool must be aligned to %#zx bytes for memory encryption\n",
+			cc_shared_granule_size());
+		return -EINVAL;
+	}
+
 	/*
 	 * Since multiple devices can share the same pool, the private data,
 	 * io_tlb_mem struct, will be initialized by the first device attached
@@ -2035,8 +2067,8 @@ static int rmem_swiotlb_device_init(struct reserved_mem *rmem,
 			int ret;
 
 			mem->cc_shared = true;
-			ret = set_memory_decrypted((unsigned long)phys_to_virt(rmem->base),
-						   rmem->size >> PAGE_SHIFT);
+			ret = cc_make_shared(phys_to_virt(rmem->base),
+					     rmem->size);
 			if (ret) {
 				dev_err(dev, "Failed to decrypt restricted DMA pool\n");
 				kfree(pool->areas);
diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
index 59fb9d71e9d86..2a337ce7264e4 100644
--- a/kernel/kexec_file.c
+++ b/kernel/kexec_file.c
@@ -682,7 +682,8 @@ static int kexec_alloc_contig(struct kexec_buf *kbuf)
 	if (kbuf->image->type == KEXEC_TYPE_CRASH)
 		return -EPERM;
 
-	p = dma_alloc_from_contiguous(NULL, nr_pages, get_order(kbuf->buf_align), true);
+	p = dma_alloc_from_contiguous(NULL, nr_pages,
+				      get_order(kbuf->buf_align), 0, true);
 	if (!p)
 		return -ENOMEM;
 
diff --git a/mm/Makefile b/mm/Makefile
index e7245cb88c665..6e65444284226 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -56,6 +56,7 @@ obj-y			:= filemap.o mempool.o oom_kill.o fadvise.o \
 			   compaction.o show_mem.o \
 			   interval_tree.o list_lru.o workingset.o \
 			   debug.o gup.o mmap_lock.o vma_init.o $(mmu-y)
+obj-y			+= cc_shared.o
 
 # Give 'page_alloc' its own module-parameter namespace
 page-alloc-y := page_alloc.o
diff --git a/mm/cc_shared.c b/mm/cc_shared.c
new file mode 100644
index 0000000000000..6f68a3f44ef80
--- /dev/null
+++ b/mm/cc_shared.c
@@ -0,0 +1,232 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 ARM Ltd.
+ */
+#include <linux/align.h>
+#include <linux/cc_platform.h>
+#include <linux/cc_shared.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/gfp.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/mm.h>
+#include <linux/mem_encrypt.h>
+#include <linux/numa.h>
+#include <linux/overflow.h>
+#include <linux/set_memory.h>
+
+size_t __weak arch_cc_shared_granule_size(void)
+{
+	return PAGE_SIZE;
+}
+
+size_t cc_shared_granule_size(void)
+{
+	size_t granule = arch_cc_shared_granule_size();
+
+	if (WARN_ON_ONCE(granule < PAGE_SIZE || !is_power_of_2(granule)))
+		return PAGE_SIZE;
+
+	return granule;
+}
+EXPORT_SYMBOL_GPL(cc_shared_granule_size);
+
+int cc_shared_calc_layout(size_t requested, struct cc_shared_layout *layout)
+{
+	size_t granule, rounded;
+
+	if (!requested || !layout)
+		return -EINVAL;
+
+	granule = cc_shared_granule_size();
+	if (check_add_overflow(requested, granule - 1, &rounded))
+		return -EOVERFLOW;
+
+	rounded = ALIGN_DOWN(rounded, granule);
+	layout->requested_size = requested;
+	layout->shared_size = rounded;
+	layout->alignment = granule;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(cc_shared_calc_layout);
+
+bool cc_shared_range_valid(phys_addr_t base, size_t size)
+{
+	size_t granule = cc_shared_granule_size();
+
+	if (!size)
+		return false;
+
+	return IS_ALIGNED(base, granule) && IS_ALIGNED(size, granule);
+}
+EXPORT_SYMBOL_GPL(cc_shared_range_valid);
+
+static int cc_validate_transition(void *addr, size_t size)
+{
+	phys_addr_t phys;
+
+	if (!addr || !size || !PAGE_ALIGNED(addr) ||
+	    !virt_addr_valid(addr))
+		return -EINVAL;
+
+	phys = page_to_phys(virt_to_page(addr));
+	if (!cc_shared_range_valid(phys, size))
+		return -EINVAL;
+
+	return 0;
+}
+
+int cc_make_shared(void *addr, size_t size)
+{
+	int ret = cc_validate_transition(addr, size);
+
+	if (ret)
+		return ret;
+
+	return set_memory_decrypted((unsigned long)addr, size >> PAGE_SHIFT);
+}
+
+int cc_make_private(void *addr, size_t size)
+{
+	int ret = cc_validate_transition(addr, size);
+
+	if (ret)
+		return ret;
+
+	return set_memory_encrypted((unsigned long)addr, size >> PAGE_SHIFT);
+}
+
+static int __alloc_cc_shared_pages_node(int nid, gfp_t gfp,
+					size_t requested,
+					struct cc_shared_pages *mem)
+{
+	struct cc_shared_layout layout;
+	struct page *page;
+	unsigned int order;
+	int ret;
+
+	might_sleep();
+
+	ret = cc_shared_calc_layout(requested, &layout);
+	if (ret)
+		return ret;
+
+	order = get_order(layout.shared_size);
+	if (order > MAX_PAGE_ORDER)
+		return -EINVAL;
+
+	/*
+	 * State transitions require a linear-map address and may modify memory.
+	 * Allocate from low memory and let the architecture place zeroing at the
+	 * appropriate point in the transition.
+	 */
+	gfp &= ~(__GFP_HIGHMEM | __GFP_ZERO);
+	if (nid == NUMA_NO_NODE)
+		page = alloc_pages(gfp, order);
+	else
+		page = alloc_pages_node(nid, gfp, order);
+	if (!page)
+		return -ENOMEM;
+
+	ret = cc_make_shared(page_address(page), layout.shared_size);
+	if (ret) {
+		if (!cc_make_private(page_address(page), layout.shared_size))
+			__free_pages(page, order);
+		else
+			pr_warn_ratelimited("leaking %zu bytes with uncertain shared state\n",
+					    layout.shared_size);
+		return ret;
+	}
+
+	mem->page = page;
+	mem->shared_size = layout.shared_size;
+	return 0;
+}
+
+/**
+ * alloc_cc_shared_pages_node - allocate memory that can be shared
+ * @nid: NUMA node from which to allocate, or %NUMA_NO_NODE
+ * @gfp: allocation flags
+ * @requested: number of bytes requested; must be nonzero
+ * @mem: storage for the allocated page and the size of the shared range
+ *
+ * Allocate at least @requested bytes and make the allocation shared when
+ * memory encryption is active.  A memory-state transition requires a valid
+ * linear-map address, so such allocations never come from high memory
+ *
+ * The shared range may be rounded up to the architecture's transition
+ * granule.  On success, @mem->shared_size records the actual size that was
+ * made shared and must be retained unchanged for free_cc_shared_pages().
+ * @mem is not modified on failure.
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+int alloc_cc_shared_pages_node(int nid, gfp_t gfp,
+			       size_t requested,
+			       struct cc_shared_pages *mem)
+{
+	struct page *page;
+	unsigned int order;
+
+	if (!mem || !requested)
+		return -EINVAL;
+
+	if (cc_platform_has(CC_ATTR_MEM_ENCRYPT))
+		return __alloc_cc_shared_pages_node(nid, gfp, requested, mem);
+
+	order = get_order(requested);
+	if (order > MAX_PAGE_ORDER)
+		return -EINVAL;
+
+	if (nid == NUMA_NO_NODE)
+		page = alloc_pages(gfp, order);
+	else
+		page = alloc_pages_node(nid, gfp, order);
+	if (!page)
+		return -ENOMEM;
+
+	mem->page = page;
+	mem->shared_size = requested;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(alloc_cc_shared_pages_node);
+
+/**
+ * alloc_cc_shared_pages - allocate memory that can be shared
+ * @gfp: allocation flags
+ * @requested: number of bytes requested; must be nonzero
+ * @mem: storage for the allocated page and the size of the shared range
+ *
+ * Equivalent to alloc_cc_shared_pages_node() with %NUMA_NO_NODE.
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+int alloc_cc_shared_pages(gfp_t gfp,
+			  size_t requested, struct cc_shared_pages *mem)
+{
+	return alloc_cc_shared_pages_node(NUMA_NO_NODE, gfp, requested, mem);
+}
+EXPORT_SYMBOL_GPL(alloc_cc_shared_pages);
+
+void free_cc_shared_pages(struct cc_shared_pages *mem)
+{
+	if (!mem || !mem->page)
+		return;
+
+	if (!cc_platform_has(CC_ATTR_MEM_ENCRYPT))
+		goto free_pages;
+
+	if (cc_make_private(page_address(mem->page), mem->shared_size)) {
+		pr_warn_ratelimited("leaking %zu bytes that cannot be made private\n",
+				    mem->shared_size);
+		return;
+	}
+
+free_pages:
+	__free_pages(mem->page, get_order(mem->shared_size));
+	mem->page = NULL;
+	mem->shared_size = 0;
+}
+EXPORT_SYMBOL_GPL(free_cc_shared_pages);

55/3 2026/09/26 15:32 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 26309
							output: 40
							thoughts: 1521
						

Thoughts:
**KMSAN Evaluation: Patch Series Analysis**

Alright, let's dive into this patch series. My task is to determine whether it warrants firing up a KMSAN fuzzing session. I need to be methodical, because KMSAN is best deployed when it will uncover something KASAN won't. The target is amd64, so I have to keep that in mind.

My strategy is clear: I need to scrutinize the code changes for potential uninitialized memory reads or information leaks to user space. If standard kernel sanitizers, like KASAN and LOCKDEP, are likely to catch bugs, then KMSAN is unnecessary.

This patch series seems to deal with confidential computing, specifically memory sharing/encryption transitions, DMA buffers, CMA, SWIOTLB, and confidential computing shared pages (`cc_shared.c`). It is clearly about securing things.

Now, let's break down these changes piece by piece. First up:

1.  `arch/arm/mm/dma-mapping.c`: Looks like a parameter change to `dma_alloc_from_contiguous`. Nothing alarming. `attrs` is initialized to zero. Skip.

2.  `arch/arm64/mm/pageattr.c`: Zeroing out memory with `memset` in `realm_set_memory_decrypted` if `!ret`. Fine, this is *intentional* zeroing, not a potential source of errors. Good.

3.  `arch/powerpc/platforms/pseries/svm.c` and
4.  `arch/s390/mm/init.c`: Same pattern as above, explicit zeroing. All good.

5.  `arch/x86/coco/tdx/tdx.c`: Calls to `memset` to zero memory based on a conditional `!enc`, looks safe.

6.  `arch/x86/hyperv/hv_init.c`: Removing `memset` because the memory is already zeroed by `set_memory_decrypted`. Excellent. I will keep an eye on x86, because this is an area which deals with memory security and encryption, and I need to verify that this is the case.

7.  `arch/x86/hyperv/ivm.c`: Another `memset` based on `!enc`. Still intentional zeroing.

8.  `arch/x86/kernel/kvmclock.c`: Aha! This is interesting. This patch *conditionally* calls `set_memory_decrypted` *or* `memset` based on the `cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)` flag. If the memory is being encrypted, `set_memory_decrypted` is used. If it's *not* being encrypted, then `memset` is called. It looks like the memory is always initialized. This warrants further investigation to be certain.

9.  `arch/x86/mm/mem_encrypt_amd.c`: Similarly, zeroing memory based on the `!enc` condition. Part of the `set_memory_decrypted` process. Looks intentional.

10. `drivers/dma-buf/heaps/system_heap.c`: This is the most complex section. This patch seems to be refactoring things to use `alloc_cc_shared_pages`/`free_cc_shared_pages`. The code iterates through pages in an `sgtable`. The code looks fine. However, what about before this patch. My concern is that the pages might not have been zeroed before. If they were not, then a vulnerability may exist for uninitialized memory or information leaks. Before the patch, there were calls to `system_heap_set_page_decrypted` for confidential computing buffers. I need to know how pages are initialized. Was it zeroed? Yes, after a bit of digging, I can see that `system_heap_set_page_decrypted` does zero out the memory, so the current changes are safe and the old code was also safe.

The patches mainly touch memory zeroing, either explicitly with `memset`, or implicitly through functions like `set_memory_decrypted` and conditional branching. After digging in the code, the initialization appears to be consistent.

**Final Verdict: NeedsKMSAN = false**

Based on this analysis, I don't see any immediate red flags that would require a KMSAN session. The patches primarily involve zeroing out memory or dealing with confidential computing and memory encryption transitions, and the changes appear to be handled safely. I do not believe the patch introduces any risk of uninitialized memory usage or info-leaks, and it's likely that KASAN or other standard bug detectors would catch any potential issues.



56/3 2026/09/26 15:32 tool grepper
0m Args:
{
  "Expression": "system_heap_allocate",
  "PathPrefix": "drivers/dma-buf/heaps/system_heap.c"
}

Results:
{
  "Output": "drivers/dma-buf/heaps/system_heap.c=368=static struct page *alloc_largest_available(unsigned long size,\n--\ndrivers/dma-buf/heaps/system_heap.c-391-\ndrivers/dma-buf/heaps/system_heap.c:392:static struct dma_buf *system_heap_allocate(struct dma_heap *heap,\ndrivers/dma-buf/heaps/system_heap.c-393-\t\t\t\t\t    unsigned long len,\n--\ndrivers/dma-buf/heaps/system_heap.c=495=static const struct dma_heap_ops system_heap_ops = {\ndrivers/dma-buf/heaps/system_heap.c:496:\t.allocate = system_heap_allocate,\ndrivers/dma-buf/heaps/system_heap.c-497-};\n"
}

57/3 2026/09/26 15:32 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 29120
							output: 43
							thoughts: 32
						

Thoughts:
Here's a first-person summary, tailored for an expert in the field:

**Diving into System Heap Allocation**

Alright, let's get down to brass tacks. I'm going to focus in on `drivers/dma-buf/heaps/system_heap.c`, specifically the region around lines 350 to 480. That's where the heart of the system heap allocation is likely to be. I expect we'll see the core logic for how the DMA-BUF framework interacts with physical memory through this system heap implementation. I am particularly interested in how the allocations are handled.

I want to see what memory-management policies are employed here. Are we talking about a simple contiguous allocation strategy, or are there more sophisticated approaches like buddy systems or segregated free lists at play? Are there any specific optimizations related to DMA-BUF's requirements, such as considerations for cache coherency or physical address contiguity? I'm eager to understand how they handle requests of different sizes. I'm also watching for any specific locking mechanisms utilized to ensure thread-safe access to the shared system heap resource. Are they using spinlocks, mutexes, or something more advanced? What considerations are made for page alignment and memory protection?

My hypothesis is that we are likely to see some careful dance between the generic DMA-BUF framework and the underlying memory allocator. The lines mentioned may show the delicate allocation/deallocation process based on certain requests. So, let's see what's really happening in that code!



58/3 2026/09/26 15:32 tool read-file
0m Args:
{
  "File": "drivers/dma-buf/heaps/system_heap.c",
  "FirstLine": 345,
  "LineCount": 80
}

Results:
{
  "Contents": " 345:\t\t.end_cpu_access = system_heap_dma_buf_end_cpu_access,\n 346:\t\t.mmap = system_heap_mmap,\n 347:\t\t.vmap = system_heap_vmap,\n 348:\t\t.vunmap = system_heap_vunmap,\n 349:\t\t.release = system_heap_dma_buf_release,\n 350:\t};\n 351:\t\n 352:\tstatic struct page *system_heap_alloc_order(unsigned int order,\n 353:\t\t     gfp_t flags, bool cc_shared)\n 354:\t{\n 355:\t\tstruct cc_shared_pages mem;\n 356:\t\n 357:\t\tif (!cc_shared)\n 358:\t\t\treturn alloc_pages(flags, order);\n 359:\t\n 360:\t\t/* The shared granule can raise the actual allocation order. */\n 361:\t\tflags |= __GFP_COMP;\n 362:\t\tif (alloc_cc_shared_pages(flags, PAGE_SIZE \u003c\u003c order, \u0026mem))\n 363:\t\t\treturn NULL;\n 364:\t\n 365:\t\treturn mem.page;\n 366:\t}\n 367:\t\n 368:\tstatic struct page *alloc_largest_available(unsigned long size,\n 369:\t\t\tunsigned int max_order, bool cc_shared)\n 370:\t{\n 371:\t\tstruct page *page;\n 372:\t\tgfp_t flags;\n 373:\t\tint i;\n 374:\t\n 375:\t\tfor (i = 0; i \u003c NUM_ORDERS; i++) {\n 376:\t\t\tif (size \u003c  (PAGE_SIZE \u003c\u003c orders[i]))\n 377:\t\t\t\tcontinue;\n 378:\t\t\tif (max_order \u003c orders[i])\n 379:\t\t\t\tcontinue;\n 380:\t\n 381:\t\t\tflags = order_flags[i];\n 382:\t\t\tif (mem_accounting)\n 383:\t\t\t\tflags |= __GFP_ACCOUNT;\n 384:\t\t\tpage = system_heap_alloc_order(orders[i], flags, cc_shared);\n 385:\t\t\tif (!page)\n 386:\t\t\t\tcontinue;\n 387:\t\t\treturn page;\n 388:\t\t}\n 389:\t\treturn NULL;\n 390:\t}\n 391:\t\n 392:\tstatic struct dma_buf *system_heap_allocate(struct dma_heap *heap,\n 393:\t\t\t\t\t\t    unsigned long len,\n 394:\t\t\t\t\t\t    u32 fd_flags,\n 395:\t\t\t\t\t\t    u64 heap_flags)\n 396:\t{\n 397:\t\tstruct system_heap_buffer *buffer;\n 398:\t\tDEFINE_DMA_BUF_EXPORT_INFO(exp_info);\n 399:\t\tstruct cc_shared_layout layout;\n 400:\t\tunsigned long size_remaining = len;\n 401:\t\tunsigned long sg_remaining = len;\n 402:\t\tunsigned int max_order = orders[0];\n 403:\t\tstruct system_heap_priv *priv = dma_heap_get_drvdata(heap);\n 404:\t\tbool cc_shared = priv-\u003ecc_shared;\n 405:\t\tstruct dma_buf *dmabuf;\n 406:\t\tstruct sg_table *table;\n 407:\t\tstruct scatterlist *sg;\n 408:\t\tstruct list_head pages;\n 409:\t\tstruct page *page, *tmp_page;\n 410:\t\tint i, ret = -ENOMEM;\n 411:\t\n 412:\t\tif (cc_shared) {\n 413:\t\t\tint err;\n 414:\t\n 415:\t\t\terr = cc_shared_calc_layout(len, \u0026layout);\n 416:\t\t\tif (err)\n 417:\t\t\t\treturn ERR_PTR(err);\n 418:\t\n 419:\t\t\tsize_remaining = layout.shared_size;\n 420:\t\t}\n 421:\t\n 422:\t\tbuffer = kzalloc_obj(*buffer);\n 423:\t\tif (!buffer)\n 424:\t\t\treturn ERR_PTR(-ENOMEM);\n"
}

59/3 2026/09/26 15:32 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 30651
							output: 42
							thoughts: 116
						

Thoughts:
**Deconstructing the Memory Allocation Flags**

Okay, hold on a second. I'm deep in `alloc_largest_available` and something just jumped out at me. I'm looking at `flags = order_flags[i];` and I need to stop right here. What is `order_flags`? This is critical for understanding the behavior, especially when dealing with these higher-order allocations.

Alright, a quick search reveals `order_flags` is defined as `static gfp_t order_flags[] = {HIGH_ORDER_GFP, HIGH_ORDER_GFP, LOW_ORDER_GFP};`. Fine, but that raises *more* questions. What the heck are `HIGH_ORDER_GFP` and `LOW_ORDER_GFP`? I need to understand what these flags represent in the context of memory allocation. It's probably going to influence the allocation strategy here.

Let's dive into the likely source. I suspect the answer is lurking somewhere in `drivers/dma-buf/heaps/system_heap.c`. Lines 40-70 should provide the necessary context, probably a definition of the flags and how they are used, or at least how the memory allocator itself handles these flags. It's time to get a more concrete understanding of this memory allocation approach.



60/3 2026/09/26 15:32 tool read-file
0m Args:
{
  "File": "drivers/dma-buf/heaps/system_heap.c",
  "FirstLine": 40,
  "LineCount": 40
}

Results:
{
  "Contents": "  40:\t};\n  41:\t\n  42:\tstruct dma_heap_attachment {\n  43:\t\tstruct device *dev;\n  44:\t\tstruct sg_table table;\n  45:\t\tstruct list_head list;\n  46:\t\tbool mapped;\n  47:\t\tbool cc_shared;\n  48:\t};\n  49:\t\n  50:\t#define cc_shared_buffer(b) (IS_ENABLED(CONFIG_DMABUF_HEAPS_SYSTEM_CC_SHARED) \u0026\u0026 \\\n  51:\t\t\t\t\t(b)-\u003ecc_shared)\n  52:\t\n  53:\t#define LOW_ORDER_GFP (GFP_HIGHUSER | __GFP_ZERO)\n  54:\t#define HIGH_ORDER_GFP  (((GFP_HIGHUSER | __GFP_ZERO | __GFP_NOWARN \\\n  55:\t\t\t\t\t| __GFP_NORETRY) \u0026 ~__GFP_RECLAIM) \\\n  56:\t\t\t\t\t| __GFP_COMP)\n  57:\tstatic gfp_t order_flags[] = {HIGH_ORDER_GFP, HIGH_ORDER_GFP, LOW_ORDER_GFP};\n  58:\t/*\n  59:\t * The selection of the orders used for allocation (1MB, 64K, 4K) is designed\n  60:\t * to match with the sizes often found in IOMMUs. Using order 4 pages instead\n  61:\t * of order 0 pages can significantly improve the performance of many IOMMUs\n  62:\t * by reducing TLB pressure and time spent updating page tables.\n  63:\t */\n  64:\tstatic const unsigned int orders[] = {8, 4, 0};\n  65:\t#define NUM_ORDERS ARRAY_SIZE(orders)\n  66:\t\n  67:\tstatic int dup_sg_table(struct sg_table *from, struct sg_table *to)\n  68:\t{\n  69:\t\tstruct scatterlist *sg, *new_sg;\n  70:\t\tint ret, i;\n  71:\t\n  72:\t\tret = sg_alloc_table(to, from-\u003eorig_nents, GFP_KERNEL);\n  73:\t\tif (ret)\n  74:\t\t\treturn ret;\n  75:\t\n  76:\t\tnew_sg = to-\u003esgl;\n  77:\t\tfor_each_sgtable_sg(from, sg, i) {\n  78:\t\t\tsg_set_page(new_sg, sg_page(sg), sg-\u003elength, sg-\u003eoffset);\n  79:\t\t\tnew_sg = sg_next(new_sg);\n"
}

61/3 2026/09/26 15:32 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 31738
							output: 36
							thoughts: 2355
						

Thoughts:
**Confidential Computing Memory Management Deep Dive**

Okay, so I've been digging into how memory allocation and zeroing are handled in the context of confidential computing within the kernel, particularly focusing on shared memory pages and how they are handled. I started by noticing that `LOW_ORDER_GFP` and `HIGH_ORDER_GFP` both have `__GFP_ZERO` set, which is standard for ensuring that allocated memory is cleared. However, when I looked at `alloc_cc_shared_pages` and its associated function `alloc_cc_shared_pages_node`, I found something interesting.

Specifically, in `__alloc_cc_shared_pages_node`, if the platform has the `CC_ATTR_MEM_ENCRYPT` attribute, there's a crucial line: `gfp &= ~(__GFP_HIGHMEM | __GFP_ZERO);`. This seems to be intentionally stripping the `__GFP_ZERO` flag. The comment here indicates the reason: "State transitions require a linear-map address and may modify memory. Allocate from low memory and let the architecture place zeroing at the appropriate point in the transition."

Intrigued, I traced where this "architecture-specific zeroing" was happening. I found several architecture-specific implementations of `set_memory_decrypted` that are called by `cc_make_shared`. These are the key functions responsible for zeroing the memory:
*   `arch/arm64/mm/pageattr.c`
*   `arch/powerpc/platforms/pseries/svm.c`
*   `arch/s390/mm/init.c`
*   `arch/x86/coco/tdx/tdx.c`
*   `arch/x86/hyperv/ivm.c`
*   `arch/x86/mm/mem_encrypt_amd.c`
*   `drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c`

So the architectures that support confidential computing, like ARM64, PowerPC, s390, x86 (TDX, AMD), and pkvm-guest, all zero the memory in their `set_memory_decrypted` implementation!

The memory is zeroed after stripping `__GFP_ZERO` flag and before calling `cc_make_shared` when the `CC_ATTR_MEM_ENCRYPT` is true! When `CC_ATTR_MEM_ENCRYPT` is false, and the caller *did* pass `__GFP_ZERO`, then pages are still zeroed by `alloc_pages`.

This leads me to believe the patch series is doing this for memory security. In confidential computing environments (SEV, TDX, Realm, etc.), when a page transitions from private to shared (decrypted), the kernel *must* ensure the page is zeroed. This prevents leakage of sensitive data from the guest to the untrusted host or hypervisor.

I also checked `dma_direct_alloc` and `dma_direct_alloc_pages` to confirm how memory is zeroed for direct memory access (DMA). It has `memset` after allocation for when `mark_mem_decrypt` is false, and when it is true, it calls `dma_set_decrypted`, which eventually calls the same `cc_make_shared` with `set_memory_decrypted`, which zero's the memory! It all boils down to security, making sure memory is zeroed before it is made visible to a less trusted entity!



62/3 2026/09/26 15:33 tool read-file
0m Args:
{
  "File": "kernel/dma/direct.c",
  "FirstLine": 445,
  "LineCount": 45
}

Results:
{
  "Contents": " 445:\t}\n 446:\t\n 447:\tstruct page *dma_direct_alloc_pages(struct device *dev, size_t size,\n 448:\t\t\tdma_addr_t *dma_handle, enum dma_data_direction dir, gfp_t gfp)\n 449:\t{\n 450:\t\tunsigned long attrs = 0;\n 451:\t\tstruct cc_shared_layout layout;\n 452:\t\tstruct page *page;\n 453:\t\tvoid *cpu_addr;\n 454:\t\tbool mark_mem_decrypt;\n 455:\t\n 456:\t\tif (force_dma_unencrypted(dev))\n 457:\t\t\tattrs |= __DMA_ATTR_ALLOC_CC_SHARED;\n 458:\t\n 459:\t\tmark_mem_decrypt = attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED;\n 460:\t\n 461:\t\tif ((attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED) \u0026\u0026 dma_direct_use_pool(dev, gfp))\n 462:\t\t\treturn dma_direct_alloc_from_pool(dev, size, dma_handle,\n 463:\t\t\t\t\t\t\t  \u0026cpu_addr, gfp, attrs);\n 464:\t\n 465:\t\tif (is_swiotlb_for_alloc(dev)) {\n 466:\t\t\tpage = dma_direct_alloc_swiotlb(dev, size, attrs);\n 467:\t\t\tif (!page)\n 468:\t\t\t\treturn NULL;\n 469:\t\n 470:\t\t\tcpu_addr = page_address(page);\n 471:\t\t\tmark_mem_decrypt = false;\n 472:\t\t\tgoto setup_page;\n 473:\t\t}\n 474:\t\n 475:\t\tif (mark_mem_decrypt) {\n 476:\t\t\tif (cc_shared_calc_layout(size, \u0026layout))\n 477:\t\t\t\treturn NULL;\n 478:\t\t\tsize = layout.shared_size;\n 479:\t\t}\n 480:\t\n 481:\t\tpage = __dma_direct_alloc_pages(dev, size, gfp, false, attrs);\n 482:\t\tif (!page)\n 483:\t\t\treturn NULL;\n 484:\t\n 485:\t\tcpu_addr = page_address(page);\n 486:\tsetup_page:\n 487:\t\tif (mark_mem_decrypt) {\n 488:\t\t\tif (dma_set_decrypted(dev, cpu_addr, size))\n 489:\t\t\t\tgoto out_leak_pages;\n"
}

63/3 2026/09/26 15:33 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 35590
							output: 36
							thoughts: 216
						

Thoughts:
**DMA Allocation Logic: A Quick Analysis**

Okay, hold on a second! I need to re-examine something. Line 465 in that driver... I see it uses `is_swiotlb_for_alloc(dev)`. That's important. If that condition evaluates to true, it calls `dma_direct_alloc_swiotlb()`.  Then, right after that allocation, `mark_mem_decrypt` is set to `false`, and we immediately jump to the `setup_page` section. The interesting part is what happens at `setup_page`: if `mark_mem_decrypt` is true then some decrypt logic, but if not the buffer is zeroed out by `memset(cpu_addr, 0, size)`.  So, that seems clear, the memory is being zeroed in this specific path.

Now, I'm getting a nagging feeling... What about calls to `dma_direct_alloc` more generally? I should probably take a look at the core direct DMA allocation code in `kernel/dma/direct.c`. Let me quickly browse lines 200-340 to see how it handles general allocation cases.



64/3 2026/09/26 15:33 tool read-file
0m Args:
{
  "File": "kernel/dma/direct.c",
  "FirstLine": 280,
  "LineCount": 60
}

Results:
{
  "Contents": " 280:\t\t\t\t * swiotlb allocations comes from pool already marked\n 281:\t\t\t\t * decrypted\n 282:\t\t\t\t */\n 283:\t\t\t\tmark_mem_decrypt = false;\n 284:\t\t\t\tgoto setup_page;\n 285:\t\t\t}\n 286:\t\t\treturn NULL;\n 287:\t\t}\n 288:\t\n 289:\t\tif (mark_mem_decrypt) {\n 290:\t\t\tif (cc_shared_calc_layout(size, \u0026layout))\n 291:\t\t\t\treturn NULL;\n 292:\t\t\tsize = layout.shared_size;\n 293:\t\t}\n 294:\t\n 295:\t\t/* we always manually zero the memory once we are done */\n 296:\t\tpage = __dma_direct_alloc_pages(dev, size, gfp \u0026 ~__GFP_ZERO,\n 297:\t\t\t\t\t\tallow_highmem, attrs);\n 298:\t\tif (!page)\n 299:\t\t\treturn NULL;\n 300:\t\n 301:\tsetup_page:\n 302:\t\t/*\n 303:\t\t * dma_alloc_contiguous can return highmem pages depending on a\n 304:\t\t * combination the cma= arguments and per-arch setup.  These need to be\n 305:\t\t * remapped to return a kernel virtual address.\n 306:\t\t */\n 307:\t\tif (PageHighMem(page)) {\n 308:\t\t\tremap = true;\n 309:\t\t\tset_uncached = false;\n 310:\t\t}\n 311:\t\n 312:\t\tif (mark_mem_decrypt) {\n 313:\t\t\tvoid *lm_addr;\n 314:\t\n 315:\t\t\tlm_addr = page_address(page);\n 316:\t\t\tif (dma_set_decrypted(dev, lm_addr, size))\n 317:\t\t\t\tgoto out_leak_pages;\n 318:\t\t}\n 319:\t\n 320:\t\tif (remap) {\n 321:\t\t\tpgprot_t prot = dma_pgprot(dev, PAGE_KERNEL, attrs);\n 322:\t\n 323:\t\t\t/* remove any dirty cache lines on the kernel alias */\n 324:\t\t\tarch_dma_prep_coherent(page, size);\n 325:\t\n 326:\t\t\t/* create a coherent mapping */\n 327:\t\t\tcpu_addr = dma_common_contiguous_remap(page, size, prot,\n 328:\t\t\t\t\t\t__builtin_return_address(0));\n 329:\t\t\tif (!cpu_addr)\n 330:\t\t\t\tgoto out_encrypt_pages;\n 331:\t\t} else {\n 332:\t\t\tcpu_addr = page_address(page);\n 333:\t\t}\n 334:\t\n 335:\t\t/* Zero after remapping because the page may be in HighMem. */\n 336:\t\tif (!mark_mem_decrypt)\n 337:\t\t\tmemset(cpu_addr, 0, size);\n 338:\t\n 339:\t\tif (set_uncached) {\n"
}

65/3 2026/09/26 15:33 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 36923
							output: 32
							thoughts: 391
						

Thoughts:
**Analyzing DMA Allocation and Memory Zeroing**

Okay, wait a minute, let's zoom in on `dma_direct_alloc`. Specifically, I need to understand how the memory is handled, and I see it's calling `__dma_direct_alloc_pages`.  On line 296, I spot this: `gfp & ~__GFP_ZERO`. Aha! `__dma_direct_alloc_pages` *doesn't* zero the pages it allocates. Interesting.

So, if it's not zeroed at the initial allocation, where does it happen?  I see a check: if `mark_mem_decrypt` is false, then on line 336, `memset(cpu_addr, 0, size)` is called, so it gets zeroed there. That makes sense. But what about the *other* case? When `mark_mem_decrypt` is true?

Ah, line 312:  If `mark_mem_decrypt` is true, we call `dma_set_decrypted`. Now, what does *that* do?  `ret = cc_make_shared(vaddr, size);` It seems to call `cc_make_shared`. Okay, and then `cc_make_shared` calls `set_memory_decrypted((unsigned long)addr, size >> PAGE_SHIFT);`.  

The critical question now is, *does* `set_memory_decrypted` always zero the memory? I need to check the implementation of `set_memory_decrypted`, especially on x86, to know for sure. I need to track down where it's implemented.



66/3 2026/09/26 15:33 tool grepper
0m Args:
{
  "Expression": "int set_memory_decrypted",
  "PathPrefix": "arch/x86"
}

Results:
{
  "Output": "arch/x86/include/asm/set_memory.h=53=int set_memory_encrypted(unsigned long addr, int numpages);\narch/x86/include/asm/set_memory.h:54:int set_memory_decrypted(unsigned long addr, int numpages);\narch/x86/include/asm/set_memory.h-55-\n--\narch/x86/mm/pat/set_memory.c=2529=EXPORT_SYMBOL_GPL(set_memory_encrypted);\narch/x86/mm/pat/set_memory.c-2530-\narch/x86/mm/pat/set_memory.c:2531:int set_memory_decrypted(unsigned long addr, int numpages)\narch/x86/mm/pat/set_memory.c-2532-{\n"
}

67/3 2026/09/26 15:33 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 37929
							output: 44
							thoughts: 29
						

Thoughts:
Okay, here's my attempt at summarizing the thought process, as requested, targeting an expert audience:

**Diving into `set_memory.c` on x86**

Alright, I'm going to take a look at the `arch/x86/mm/pat/set_memory.c` file, specifically around line 2531. I'm probably digging into some memory management details, perhaps concerning page attribute tables (PAT) on x86, or maybe some aspects of how memory permissions are being set or modified. Given the file and function name, my first guess is this will likely relate to how we're dealing with caching attributes at a lower level than the virtual memory manager (VMM) might normally handle. I'm thinking about the low-level aspects of caching control, such as Write-Combining, Write-Through, or Write-Back memory access patterns.

I'm expecting to find code that interacts directly with the MSRs responsible for the PAT functionality, maybe setting or clearing bits within these registers. It's also likely I'll be looking at how specific memory regions are being remapped or altered based on the PAT configuration. If it's related to setting or modifying the memory attribute of a given area, I want to see if this is being called from the upper layers of the VMM, or if there is another part of the system is the one setting the attributes on its own. I'm bracing for some bit-twiddling and potentially some architecture-specific considerations relating to CPU model and feature flags. This section of code likely provides the primitives upon which more complex memory operations are built, I'm expecting some interactions with the CPU's cache or memory controller, potentially related to cache coherency and memory ordering if we are doing anything multiprocessor related.



68/3 2026/09/26 15:33 tool read-file
0m Args:
{
  "File": "arch/x86/mm/pat/set_memory.c",
  "FirstLine": 2525,
  "LineCount": 40
}

Results:
{
  "Contents": "2525:\tint set_memory_encrypted(unsigned long addr, int numpages)\n2526:\t{\n2527:\t\treturn __set_memory_enc_dec(addr, numpages, true);\n2528:\t}\n2529:\tEXPORT_SYMBOL_GPL(set_memory_encrypted);\n2530:\t\n2531:\tint set_memory_decrypted(unsigned long addr, int numpages)\n2532:\t{\n2533:\t\treturn __set_memory_enc_dec(addr, numpages, false);\n2534:\t}\n2535:\tEXPORT_SYMBOL_GPL(set_memory_decrypted);\n2536:\t\n2537:\tint set_pages_uc(struct page *page, int numpages)\n2538:\t{\n2539:\t\tunsigned long addr = (unsigned long)page_address(page);\n2540:\t\n2541:\t\treturn set_memory_uc(addr, numpages);\n2542:\t}\n2543:\tEXPORT_SYMBOL(set_pages_uc);\n2544:\t\n2545:\tstatic int _set_pages_array(struct page **pages, int numpages,\n2546:\t\t\tenum page_cache_mode new_type)\n2547:\t{\n2548:\t\tunsigned long start;\n2549:\t\tunsigned long end;\n2550:\t\tenum page_cache_mode set_type;\n2551:\t\tint i;\n2552:\t\tint free_idx;\n2553:\t\tint ret;\n2554:\t\n2555:\t\tfor (i = 0; i \u003c numpages; i++) {\n2556:\t\t\tif (PageHighMem(pages[i]))\n2557:\t\t\t\tcontinue;\n2558:\t\t\tstart = page_to_pfn(pages[i]) \u003c\u003c PAGE_SHIFT;\n2559:\t\t\tend = start + PAGE_SIZE;\n2560:\t\t\tif (memtype_reserve(start, end, new_type, NULL))\n2561:\t\t\t\tgoto err_out;\n2562:\t\t}\n2563:\t\n2564:\t\t/* If WC, set to UC- first and then WC */\n"
}

69/3 2026/09/26 15:33 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 38972
							output: 43
							thoughts: 34
						

Thoughts:
Okay, let's dive into this. Here's my take on understanding `__set_memory_enc_dec` in `arch/x86/mm/pat/set_memory.c`:

**Deep Dive into `__set_memory_enc_dec` in `arch/x86/mm/pat/set_memory.c`**

Alright, I need to get a clear picture of what this function, `__set_memory_enc_dec`, is actually *doing*. Based on the context, and what I know about the architecture, I'm guessing this function is deeply involved in managing memory, likely tied to Page Attribute Table (PAT) and dealing with memory encoding and decoding. It makes perfect sense that it's located under the `x86` architecture directory. PAT is crucial for optimizing memory access by controlling attributes like caching and write policies. Given the "enc_dec" aspect, I suspect this function might handle the specific task of modifying PAT entries, and translating between the encoded representation (used internally for hardware interaction) and a more human-readable or manageable form (used by the kernel). I will be looking closely at how it modifies the memory map or potentially how it manages memory mapping for encryption and/or decryption. I need to focus on any bitwise operations it performs, and how those map to the specific PAT bits and their associated meanings in the x86 architecture. I'm also anticipating that it likely involves critical section management or some form of synchronization to ensure atomicity, given the potential for concurrent access to the PAT. My attention will be on looking for any locking primitives that are involved.



70/3 2026/09/26 15:33 tool grepper
0m Args:
{
  "Expression": "__set_memory_enc_dec",
  "PathPrefix": "arch/x86/mm/pat/set_memory.c"
}

Results:
{
  "Output": "arch/x86/mm/pat/set_memory.c=2495=bool set_memory_enc_stop_conversion(void)\n--\narch/x86/mm/pat/set_memory.c-2508-\narch/x86/mm/pat/set_memory.c:2509:static int __set_memory_enc_dec(unsigned long addr, int numpages, bool enc)\narch/x86/mm/pat/set_memory.c-2510-{\n--\narch/x86/mm/pat/set_memory.c=2525=int set_memory_encrypted(unsigned long addr, int numpages)\narch/x86/mm/pat/set_memory.c-2526-{\narch/x86/mm/pat/set_memory.c:2527:\treturn __set_memory_enc_dec(addr, numpages, true);\narch/x86/mm/pat/set_memory.c-2528-}\n--\narch/x86/mm/pat/set_memory.c=2531=int set_memory_decrypted(unsigned long addr, int numpages)\narch/x86/mm/pat/set_memory.c-2532-{\narch/x86/mm/pat/set_memory.c:2533:\treturn __set_memory_enc_dec(addr, numpages, false);\narch/x86/mm/pat/set_memory.c-2534-}\n"
}

71/3 2026/09/26 15:33 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 39775
							output: 44
							thoughts: 33
						

Thoughts:
Okay, let's dive into this. Here's my take on those lines:

**Analyzing `arch/x86/mm/pat/set_memory.c` (Lines 2500-2525)**

Alright, so I'm focusing in on lines 2500-2525 of `arch/x86/mm/pat/set_memory.c`. This section likely deals with a core aspect of memory management on x86, probably related to Page Attribute Table (PAT) manipulation, given the file path. Considering this specific range, I'm expecting to find a granular function or a significant code block that deals with the configuration of memory attributes. Perhaps we're setting up the PAT entries for a specific memory region or handling a transition between memory types. It's likely involved with ensuring memory is correctly marked as cacheable or non-cacheable. Let me quickly refresh my memory on the surrounding code, just to get the context right. I need to understand where this fits into the broader picture of setting up and modifying memory mappings. It's crucial, obviously, that I keep in mind the different CPU cache levels and the interaction between the PAT and the Translation Lookaside Buffer (TLB). This is where things can get tricky. I need to pay close attention to the use of memory barriers or cache flushes if they're present. I'm also betting this section will contain some CPU-specific assembly instructions, or at least calls to functions that handle the low-level manipulation of the PAT registers. Understanding the implications for performance is key, so I'll be keeping an eye out for any optimizations or potentially inefficient operations. Okay, let's see what's actually there.



72/3 2026/09/26 15:33 tool read-file
0m Args:
{
  "File": "arch/x86/mm/pat/set_memory.c",
  "FirstLine": 2500,
  "LineCount": 30
}

Results:
{
  "Contents": "2500:\t\t */\n2501:\t\tif (oops_in_progress)\n2502:\t\t\treturn down_write_trylock(\u0026mem_enc_lock);\n2503:\t\n2504:\t\tdown_write(\u0026mem_enc_lock);\n2505:\t\n2506:\t\treturn true;\n2507:\t}\n2508:\t\n2509:\tstatic int __set_memory_enc_dec(unsigned long addr, int numpages, bool enc)\n2510:\t{\n2511:\t\tint ret = 0;\n2512:\t\n2513:\t\tif (cc_platform_has(CC_ATTR_MEM_ENCRYPT)) {\n2514:\t\t\tif (!down_read_trylock(\u0026mem_enc_lock))\n2515:\t\t\t\treturn -EBUSY;\n2516:\t\n2517:\t\t\tret = __set_memory_enc_pgtable(addr, numpages, enc);\n2518:\t\n2519:\t\t\tup_read(\u0026mem_enc_lock);\n2520:\t\t}\n2521:\t\n2522:\t\treturn ret;\n2523:\t}\n2524:\t\n2525:\tint set_memory_encrypted(unsigned long addr, int numpages)\n2526:\t{\n2527:\t\treturn __set_memory_enc_dec(addr, numpages, true);\n2528:\t}\n2529:\tEXPORT_SYMBOL_GPL(set_memory_encrypted);\n"
}

73/3 2026/09/26 15:33 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 40626
							output: 31
							thoughts: 308
						

Thoughts:
**Analyzing Memory Encryption and DMA Decryption**

Okay, here we go. Let's dig into line 2513 – that memory encryption check is the starting point. I see the `cc_platform_has(CC_ATTR_MEM_ENCRYPT)` check. If it *is* present, we attempt to lock the memory encryption lock, call `__set_memory_enc_pgtable`, and unlock. Standard. But wait a minute... what happens if `CC_ATTR_MEM_ENCRYPT` is *not* set? What does that call to `__set_memory_enc_dec` actually *do*? Oh, right, it returns zero – it effectively does nothing! Alright, that’s good to keep in mind for optimization later.

Now, let's switch gears slightly. If memory encryption isn't happening via the primary path, what about DMA decryption? That's the next logical area. What's the relationship between the encryption state and the Direct Memory Access (DMA)? I see `dma_set_decrypted` - that's the key. Where is that set? It appears to be linked to `mark_mem_decrypt`. And that is tied to `attrs & __DMA_ATTR_ALLOC_CC_SHARED`. When is `mark_mem_decrypt` actually set? Ah, it depends on `force_dma_unencrypted(dev)`. Okay, we need to know when *that* returns true. Time to check `force_dma_unencrypted` itself. Let's delve in.



74/3 2026/09/26 15:33 tool grepper
0m Args:
{
  "Expression": "force_dma_unencrypted",
  "PathPrefix": "kernel/dma"
}

Results:
{
  "Output": "kernel/dma/direct.c=42=u64 dma_direct_get_required_mask(struct device *dev)\nkernel/dma/direct.c-43-{\nkernel/dma/direct.c:44:\tbool require_decrypted = force_dma_unencrypted(dev);\nkernel/dma/direct.c-45-\tphys_addr_t phys = ((phys_addr_t)max_pfn \u003c\u003c PAGE_SHIFT) - 1;\n--\nkernel/dma/direct.c=73=bool dma_coherent_ok(struct device *dev, phys_addr_t phys, size_t size)\nkernel/dma/direct.c-74-{\nkernel/dma/direct.c:75:\tbool require_decrypted = force_dma_unencrypted(dev);\nkernel/dma/direct.c-76-\tdma_addr_t dma_addr = phys_to_dma_direct(dev, phys, require_decrypted);\n--\nkernel/dma/direct.c=202=void *dma_direct_alloc(struct device *dev, size_t size,\n--\nkernel/dma/direct.c-211-\nkernel/dma/direct.c:212:\tif (force_dma_unencrypted(dev))\nkernel/dma/direct.c-213-\t\tattrs |= __DMA_ATTR_ALLOC_CC_SHARED;\n--\nkernel/dma/direct.c=369=void dma_direct_free(struct device *dev, size_t size,\n--\nkernel/dma/direct.c-381-\t */\nkernel/dma/direct.c:382:\tif (force_dma_unencrypted(dev))\nkernel/dma/direct.c-383-\t\tattrs |= __DMA_ATTR_ALLOC_CC_SHARED;\n--\nkernel/dma/direct.c=447=struct page *dma_direct_alloc_pages(struct device *dev, size_t size,\n--\nkernel/dma/direct.c-455-\nkernel/dma/direct.c:456:\tif (force_dma_unencrypted(dev))\nkernel/dma/direct.c-457-\t\tattrs |= __DMA_ATTR_ALLOC_CC_SHARED;\n--\nkernel/dma/direct.c=500=void dma_direct_free_pages(struct device *dev, size_t size,\n--\nkernel/dma/direct.c-511-\t */\nkernel/dma/direct.c:512:\tbool mark_mem_encrypted = force_dma_unencrypted(dev);\nkernel/dma/direct.c-513-\n--\nkernel/dma/direct.c=676=int dma_direct_mmap(struct device *dev, struct vm_area_struct *vma,\n--\nkernel/dma/direct.c-686-\nkernel/dma/direct.c:687:\tif (force_dma_unencrypted(dev))\nkernel/dma/direct.c-688-\t\tattrs |= DMA_ATTR_CC_SHARED;\n--\nkernel/dma/direct.c=831=size_t dma_direct_max_mapping_size(struct device *dev)\n--\nkernel/dma/direct.c-835-\t    (dma_addressing_limited(dev) || is_swiotlb_force_bounce(dev) ||\nkernel/dma/direct.c:836:\t     force_dma_unencrypted(dev)))\nkernel/dma/direct.c-837-\t\treturn swiotlb_max_mapping_size(dev);\n--\nkernel/dma/mapping.c=633=void *dma_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle,\n--\nkernel/dma/mapping.c-654-\nkernel/dma/mapping.c:655:\tif (force_dma_unencrypted(dev))\nkernel/dma/mapping.c-656-\t\tattrs |= __DMA_ATTR_ALLOC_CC_SHARED;\n--\nkernel/dma/swiotlb.c=1554=phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr,\n--\nkernel/dma/swiotlb.c-1578-\t\t/* swiotlb pool is incorrect for this device */\nkernel/dma/swiotlb.c:1579:\t\tif (unlikely(mem-\u003ecc_shared != force_dma_unencrypted(dev)))\nkernel/dma/swiotlb.c-1580-\t\t\treturn (phys_addr_t)DMA_MAPPING_ERROR;\n"
}

75/3 2026/09/26 15:33 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 42482
							output: 22
							thoughts: 16
						

Thoughts:
Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:

**Initial Inquiry on `force_dma_unencrypted`**

Right, let's get straight to the point. The first thing that pops into my mind when I encounter a term like `force_dma_unencrypted` is, "What exactly is this about?". I need to immediately understand the context and implications of this. It strongly suggests a setting or mechanism that either *disables* encryption for Direct Memory Access (DMA) transactions, or *forces* the system to operate in a potentially less secure mode. I need to understand why someone would even *consider* such a thing, and what the potential benefits or trade-offs might be.

My immediate follow-up questions would be:

*   What hardware is this relevant to? Is it a specific platform (e.g., a particular chipset, NIC, or storage controller), or is it more broadly applicable?
*   What's the *default* behavior? Is encryption the normal state, and this is an override? Or is it the other way around?
*   What are the security implications? This is crucial. If DMA encryption is disabled or bypassed, this opens the door to potential data breaches, as I know.
*   What performance implications does this have? Are there specific scenarios where the overhead of encryption is a bottleneck, and a performance boost is *actually* achieved?
*   Are there any documented use-cases or reasons why someone might need to use this function?

My goal here is to rapidly build a precise mental model of this `force_dma_unencrypted` function and its implications within the context of hardware security and performance.



76/3 2026/09/26 15:33 tool grepper
0m Args:
{
  "Expression": "bool force_dma_unencrypted"
}

Results:
{
  "Output": "arch/arm64/include/asm/mem_encrypt.h=20=int realm_register_memory_enc_ops(void);\narch/arm64/include/asm/mem_encrypt.h-21-\narch/arm64/include/asm/mem_encrypt.h:22:static inline bool force_dma_unencrypted(struct device *dev)\narch/arm64/include/asm/mem_encrypt.h-23-{\n--\narch/powerpc/include/asm/mem_encrypt.h=14=struct device;\narch/powerpc/include/asm/mem_encrypt.h-15-\narch/powerpc/include/asm/mem_encrypt.h:16:static inline bool force_dma_unencrypted(struct device *dev)\narch/powerpc/include/asm/mem_encrypt.h-17-{\n--\narch/s390/mm/init.c=129=int set_memory_decrypted(unsigned long vaddr, int numpages)\n--\narch/s390/mm/init.c-143-/* are we a protected virtualization guest? */\narch/s390/mm/init.c:144:bool force_dma_unencrypted(struct device *dev)\narch/s390/mm/init.c-145-{\n--\narch/x86/mm/mem_encrypt.c-19-/* Override for DMA direct allocation check - ARCH_HAS_FORCE_DMA_UNENCRYPTED */\narch/x86/mm/mem_encrypt.c:20:bool force_dma_unencrypted(struct device *dev)\narch/x86/mm/mem_encrypt.c-21-{\n--\ninclude/linux/dma-direct.h=114=static inline phys_addr_t dma_to_phys(struct device *dev, dma_addr_t dma_addr)\n--\ninclude/linux/dma-direct.h-128-#ifdef CONFIG_ARCH_HAS_FORCE_DMA_UNENCRYPTED\ninclude/linux/dma-direct.h:129:bool force_dma_unencrypted(struct device *dev);\ninclude/linux/dma-direct.h-130-#else\ninclude/linux/dma-direct.h:131:static inline bool force_dma_unencrypted(struct device *dev)\ninclude/linux/dma-direct.h-132-{\n"
}

77/3 2026/09/26 15:33 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 43486
							output: 40
							thoughts: 22
						

Thoughts:
Okay, here's my interpretation of what's going on, framed as a first-person thought process, keeping in mind the expert user:

**Diving into `mem_encrypt.c:20` - A Critical Examination**

Alright, let's zero in on `arch/x86/mm/mem_encrypt.c:20`. Right off the bat, I know this is kernel memory encryption code. My spider sense is tingling; this is likely where the magic happens, or more often, where the headaches begin, when dealing with Secure Memory Encryption (SME) or Total Memory Encryption (TME) on x86 platforms. I need to understand what this specific line is doing. Is it setting up the encryption keys? Initializing the memory regions? Or perhaps, it's involved in handling the page faults related to encryption? I need to quickly determine the context to determine the critical functions and variables in play.

I'm probably looking at key management, potentially at where the encryption context is established on memory allocation, or, worse, where a subtle race condition in encryption is lurking. This is a sensitive area. Improper key handling or a flawed algorithm could introduce vulnerabilities that can easily be overlooked. I'll need to confirm that encryption is being enforced and that the keys can't be tampered with.

My goal right now is to grasp the core functionality implemented at this line and quickly trace backward and forward to assess its impact on the system’s overall security posture. This is a critical building block, so it needs to be bulletproof.



78/3 2026/09/26 15:33 tool read-file
0m Args:
{
  "File": "arch/x86/mm/mem_encrypt.c",
  "FirstLine": 15,
  "LineCount": 25
}

Results:
{
  "Contents": "  15:\t#include \u003clinux/virtio_anchor.h\u003e\n  16:\t\n  17:\t#include \u003casm/sev.h\u003e\n  18:\t\n  19:\t/* Override for DMA direct allocation check - ARCH_HAS_FORCE_DMA_UNENCRYPTED */\n  20:\tbool force_dma_unencrypted(struct device *dev)\n  21:\t{\n  22:\t\t/*\n  23:\t\t * For SEV, all DMA must be to unencrypted addresses.\n  24:\t\t */\n  25:\t\tif (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT))\n  26:\t\t\treturn true;\n  27:\t\n  28:\t\t/*\n  29:\t\t * For SME, all DMA must be to unencrypted addresses if the\n  30:\t\t * device does not support DMA to addresses that include the\n  31:\t\t * encryption mask.\n  32:\t\t */\n  33:\t\tif (cc_platform_has(CC_ATTR_HOST_MEM_ENCRYPT)) {\n  34:\t\t\tu64 dma_enc_mask = DMA_BIT_MASK(__ffs64(sme_me_mask));\n  35:\t\t\tu64 dma_dev_mask = min_not_zero(dev-\u003ecoherent_dma_mask,\n  36:\t\t\t\t\t\t\tdev-\u003ebus_dma_limit);\n  37:\t\n  38:\t\t\tif (dma_dev_mask \u003c= dma_enc_mask)\n  39:\t\t\t\treturn true;\n"
}

79/3 2026/09/26 15:33 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 44266
							output: 34
							thoughts: 200
						

Thoughts:
**My Initial Thoughts on Memory Encryption**

Okay, so I'm looking at this code and a potential conflict has popped up. I've noticed something interesting: it seems that when we're dealing with AMD's Secure Memory Encryption (SME), represented by `CC_ATTR_HOST_MEM_ENCRYPT` being true, the `force_dma_unencrypted` flag can *also* be true. Now, that raises a red flag. Hold on a second, what precisely *does* happen when SME is active?

My next thought jumps to: does SME automatically set the general memory encryption attribute, represented by `CC_ATTR_MEM_ENCRYPT`? The answer seems to be yes! It appears `CC_ATTR_MEM_ENCRYPT` is true if *either* host or guest encryption is active. Now that makes sense; that seems to be how it's designed.

Now, my mind goes to the `__set_memory_enc_dec` function, specifically line 2513 in `arch/x86/mm/pat/set_memory.c`. I see: `if (cc_platform_has(CC_ATTR_MEM_ENCRYPT)) ret = __set_memory_enc_pgtable(addr, numpages, enc);`. That's where the magic happens, and a decision is made based on the presence of encryption. So, my next logical step is to delve into what `__set_memory_enc_pgtable` actually *does*. I need to understand this function to fully grasp the implications of this condition.



80/3 2026/09/26 15:33 tool grepper
0m Args:
{
  "Expression": "__set_memory_enc_pgtable",
  "PathPrefix": "arch/x86"
}

Results:
{
  "Output": "arch/x86/mm/pat/set_memory.c=2413=int set_memory_global(unsigned long addr, int numpages)\n--\narch/x86/mm/pat/set_memory.c-2419-/*\narch/x86/mm/pat/set_memory.c:2420: * __set_memory_enc_pgtable() is used for the hypervisors that get\narch/x86/mm/pat/set_memory.c-2421- * informed about \"encryption\" status via page tables.\narch/x86/mm/pat/set_memory.c-2422- */\narch/x86/mm/pat/set_memory.c:2423:static int __set_memory_enc_pgtable(unsigned long addr, int numpages, bool enc)\narch/x86/mm/pat/set_memory.c-2424-{\n--\narch/x86/mm/pat/set_memory.c=2509=static int __set_memory_enc_dec(unsigned long addr, int numpages, bool enc)\n--\narch/x86/mm/pat/set_memory.c-2516-\narch/x86/mm/pat/set_memory.c:2517:\t\tret = __set_memory_enc_pgtable(addr, numpages, enc);\narch/x86/mm/pat/set_memory.c-2518-\n"
}

81/3 2026/09/26 15:33 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 45210
							output: 44
							thoughts: 26
						

Thoughts:
Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:

**Diving into the PAT Implementation: `set_memory.c:2420`**

Alright, let's get down to brass tacks. I'm focusing my attention on the `arch/x86/mm/pat/set_memory.c` file, specifically line 2420. This is where the real meat of the Page Attribute Table (PAT) manipulation happens. I need to understand exactly what's going on at this point in the memory management routine. Given the context, I'm expecting to find either a critical path in setting PAT entries, a potential optimization opportunity, or perhaps a tricky edge case related to memory mapping or caching policies. Depending on the function call, or surrounding code, it could be a crucial function for something in my speciality. I need to see *how* the PAT entries are being updated – the mechanics of the write, the locking involved (if any), and the interaction with other memory management structures like the page tables and the TLB. Is this directly modifying MTRR entries, or is it working with the PAT directly, and if so, how is the consistency with other memory attributes maintained? The details here will be key to understanding the overall performance characteristics and potential pitfalls of this particular system call/memory operation. I'm hoping it's a relatively clean implementation, so I can see what I would be able to do.



82/3 2026/09/26 15:33 tool read-file
0m Args:
{
  "File": "arch/x86/mm/pat/set_memory.c",
  "FirstLine": 2420,
  "LineCount": 45
}

Results:
{
  "Contents": "2420:\t * __set_memory_enc_pgtable() is used for the hypervisors that get\n2421:\t * informed about \"encryption\" status via page tables.\n2422:\t */\n2423:\tstatic int __set_memory_enc_pgtable(unsigned long addr, int numpages, bool enc)\n2424:\t{\n2425:\t\tpgprot_t empty = __pgprot(0);\n2426:\t\tstruct cpa_data cpa;\n2427:\t\tint ret;\n2428:\t\n2429:\t\t/* Should not be working on unaligned addresses */\n2430:\t\tif (WARN_ONCE(addr \u0026 ~PAGE_MASK, \"misaligned address: %#lx\\n\", addr))\n2431:\t\t\taddr \u0026= PAGE_MASK;\n2432:\t\n2433:\t\tmemset(\u0026cpa, 0, sizeof(cpa));\n2434:\t\tcpa.vaddr = \u0026addr;\n2435:\t\tcpa.numpages = numpages;\n2436:\t\tcpa.mask_set = enc ? pgprot_encrypted(empty) : pgprot_decrypted(empty);\n2437:\t\tcpa.mask_clr = enc ? pgprot_decrypted(empty) : pgprot_encrypted(empty);\n2438:\t\tcpa.pgd = init_mm.pgd;\n2439:\t\n2440:\t\t/* Must avoid aliasing mappings in the highmem code */\n2441:\t\tkmap_flush_unused();\n2442:\t\tvm_unmap_aliases();\n2443:\t\n2444:\t\t/* Flush the caches as needed before changing the encryption attribute. */\n2445:\t\tif (x86_platform.guest.enc_tlb_flush_required(enc))\n2446:\t\t\tcpa_flush(\u0026cpa, x86_platform.guest.enc_cache_flush_required());\n2447:\t\n2448:\t\t/* Notify hypervisor that we are about to set/clr encryption attribute. */\n2449:\t\tret = x86_platform.guest.enc_status_change_prepare(addr, numpages, enc);\n2450:\t\tif (ret)\n2451:\t\t\tgoto vmm_fail;\n2452:\t\n2453:\t\tret = __change_page_attr_set_clr(\u0026cpa, 1);\n2454:\t\n2455:\t\t/*\n2456:\t\t * After changing the encryption attribute, we need to flush TLBs again\n2457:\t\t * in case any speculative TLB caching occurred (but no need to flush\n2458:\t\t * caches again).  We could just use cpa_flush_all(), but in case TLB\n2459:\t\t * flushing gets optimized in the cpa_flush() path use the same logic\n2460:\t\t * as above.\n2461:\t\t */\n2462:\t\tcpa_flush(\u0026cpa, 0);\n2463:\t\n2464:\t\tif (ret)\n"
}

83/3 2026/09/26 15:33 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 46380
							output: 32
							thoughts: 87
						

Thoughts:
Okay, here's my take on those lines of code and the potential problem, summarized as my own thought process:

**Potential SME Compatibility Issue**

Alright, focusing in on line 2449... `ret = x86_platform.guest.enc_status_change_prepare(addr, numpages, enc);`.  Hang on a minute! That's assuming a guest environment, isn't it? The `guest` part of `x86_platform.guest` *screams* "virtual machine!" But, what happens if SME is enabled on the *bare metal host*? Is this code even designed to function correctly in that scenario? My concern is that `x86_platform.guest.enc_status_change_prepare` might behave very differently – or potentially even fail – when not operating within a virtualized guest environment. I need to investigate what this function *actually* does when it's *not* a guest, and whether that impacts the overall security and functionality of the system. Let's dig deeper and see.



84/3 2026/09/26 15:33 tool grepper
0m Args:
{
  "Expression": "enc_status_change_prepare",
  "PathPrefix": "arch/x86"
}

Results:
{
  "Output": "arch/x86/coco/tdx/tdx.c=963=static bool tdx_enc_status_changed(unsigned long vaddr, int numpages, bool enc)\n--\narch/x86/coco/tdx/tdx.c-977-\narch/x86/coco/tdx/tdx.c:978:static int tdx_enc_status_change_prepare(unsigned long vaddr, int numpages,\narch/x86/coco/tdx/tdx.c-979-\t\t\t\t\t bool enc)\n--\narch/x86/coco/tdx/tdx.c=1115=void __init tdx_early_init(void)\n--\narch/x86/coco/tdx/tdx.c-1153-\t *\narch/x86/coco/tdx/tdx.c:1154:\t * guest.enc_status_change_prepare() converts the page from\narch/x86/coco/tdx/tdx.c-1155-\t * shared=\u003eprivate before the mapping becomes private.\n--\narch/x86/coco/tdx/tdx.c-1163-\t */\narch/x86/coco/tdx/tdx.c:1164:\tx86_platform.guest.enc_status_change_prepare = tdx_enc_status_change_prepare;\narch/x86/coco/tdx/tdx.c-1165-\tx86_platform.guest.enc_status_change_finish  = tdx_enc_status_change_finish;\n--\narch/x86/hyperv/ivm.c=861=void __init hv_vtom_init(void)\n--\narch/x86/hyperv/ivm.c-896-\tx86_platform.guest.enc_tlb_flush_required = hv_vtom_tlb_flush_required;\narch/x86/hyperv/ivm.c:897:\tx86_platform.guest.enc_status_change_prepare = hv_vtom_clear_present;\narch/x86/hyperv/ivm.c-898-\tx86_platform.guest.enc_status_change_finish = hv_vtom_set_host_visibility;\n--\narch/x86/include/asm/x86_init.h=142=struct x86_init_acpi {\n--\narch/x86/include/asm/x86_init.h-150- *\narch/x86/include/asm/x86_init.h:151: * @enc_status_change_prepare:\tNotify HV before the encryption status of a range is changed\narch/x86/include/asm/x86_init.h-152- * @enc_status_change_finish:\tNotify HV after the encryption status of a range is changed\n--\narch/x86/include/asm/x86_init.h=164=struct x86_guest {\narch/x86/include/asm/x86_init.h:165:\tint (*enc_status_change_prepare)(unsigned long vaddr, int npages, bool enc);\narch/x86/include/asm/x86_init.h-166-\tint (*enc_status_change_finish)(unsigned long vaddr, int npages, bool enc);\n--\narch/x86/kernel/x86_init.c=139=static void default_nmi_init(void) { };\narch/x86/kernel/x86_init.c-140-\narch/x86/kernel/x86_init.c:141:static int enc_status_change_prepare_noop(unsigned long vaddr, int npages, bool enc) { return 0; }\narch/x86/kernel/x86_init.c-142-static int enc_status_change_finish_noop(unsigned long vaddr, int npages, bool enc) { return 0; }\n--\narch/x86/kernel/x86_init.c=149=struct x86_platform_ops x86_platform __ro_after_init = {\n--\narch/x86/kernel/x86_init.c-165-\t.guest = {\narch/x86/kernel/x86_init.c:166:\t\t.enc_status_change_prepare = enc_status_change_prepare_noop,\narch/x86/kernel/x86_init.c-167-\t\t.enc_status_change_finish  = enc_status_change_finish_noop,\n--\narch/x86/mm/mem_encrypt_amd.c=257=static void enc_dec_hypercall(unsigned long vaddr, unsigned long size, bool enc)\n--\narch/x86/mm/mem_encrypt_amd.c-286-\narch/x86/mm/mem_encrypt_amd.c:287:static int amd_enc_status_change_prepare(unsigned long vaddr, int npages, bool enc)\narch/x86/mm/mem_encrypt_amd.c-288-{\n--\narch/x86/mm/mem_encrypt_amd.c=481=void __init sme_early_init(void)\n--\narch/x86/mm/mem_encrypt_amd.c-492-\narch/x86/mm/mem_encrypt_amd.c:493:\tx86_platform.guest.enc_status_change_prepare = amd_enc_status_change_prepare;\narch/x86/mm/mem_encrypt_amd.c-494-\tx86_platform.guest.enc_status_change_finish  = amd_enc_status_change_finish;\n--\narch/x86/mm/pat/set_memory.c=2423=static int __set_memory_enc_pgtable(unsigned long addr, int numpages, bool enc)\n--\narch/x86/mm/pat/set_memory.c-2448-\t/* Notify hypervisor that we are about to set/clr encryption attribute. */\narch/x86/mm/pat/set_memory.c:2449:\tret = x86_platform.guest.enc_status_change_prepare(addr, numpages, enc);\narch/x86/mm/pat/set_memory.c-2450-\tif (ret)\n"
}

85/3 2026/09/26 15:33 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 48346
							output: 33
							thoughts: 1505
						

Thoughts:
**Deep Dive into DMA Allocation with Memory Encryption**

Okay, so I'm digging into how DMA allocation works in the kernel, specifically when memory encryption features like AMD SME/SEV, Intel TDX, or Hyper-V are in play. I started by looking at `arch/x86/mm/mem_encrypt_amd.c:481`. I see that `sme_early_init` sets `x86_platform.guest.enc_status_change_prepare` to `amd_enc_status_change_prepare`. This function zeroes memory when encryption is being disabled, which makes sense for those platforms. The same pattern is present for Intel TDX with `tdx_enc_status_change_prepare` and Hyper-V with `hv_vtom_clear_present`. All of them zero memory when disabling encryption.

But here's where it gets interesting. What if none of those platforms are active? Let's say we're on a standard x86 kernel or even a standard QEMU VM. In that case, what does `cc_platform_has(CC_ATTR_MEM_ENCRYPT)` return? It's going to return `false`, which means memory encryption isn't considered active.

Now, let's explore `force_dma_unencrypted`. On x86, this function is critical. If `cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)` is true (meaning SEV is active) it returns `true`. If `cc_platform_has(CC_ATTR_HOST_MEM_ENCRYPT)` is true (meaning SME is active) and the device's DMA mask is compatible with the encryption mask, it also returns `true`. Otherwise, it returns `false`.

So, if memory encryption is *not* active, `force_dma_unencrypted` returns `false`. This means that the `attrs & __DMA_ATTR_ALLOC_CC_SHARED` bit will be zero, and `mark_mem_decrypt` will be `false`.

Now, if `mark_mem_decrypt` is false, in `dma_direct_alloc`, `memset(cpu_addr, 0, size);` is going to be executed. In this case, even without encryption, the memory will be zeroed, which is what I suspected. It will also be zeroed in `dma_alloc_from_contiguous` and `dma_alloc_contiguous`.

However, the more pressing question is: is there any situation where `mark_mem_decrypt` is `true`, but the `set_memory_decrypted` routine *doesn't* zero the memory?

I've got to investigate `enc_status_change_prepare_noop`. Under the default x86 platform, this function just returns 0. When would this function be called? It appears to be called only if `cc_platform_has(CC_ATTR_MEM_ENCRYPT)` is true. If that's the case but neither `sme_early_init`, `tdx_early_init`, nor `hv_vtom_init` ran, the system is supposed to run this function. So, is there any situation where `CC_ATTR_MEM_ENCRYPT` is true without any of those? I need to look into the implementation of `cc_platform_has`. I'll have to go over those implementations to make sure this is the case.



86/3 2026/09/26 15:33 tool grepper
0m Args:
{
  "Expression": "CC_ATTR_MEM_ENCRYPT",
  "PathPrefix": "arch/x86"
}

Results:
{
  "Output": "arch/x86/coco/core.c=30=static bool noinstr intel_cc_platform_has(enum cc_attr attr)\n--\narch/x86/coco/core.c-34-\tcase CC_ATTR_GUEST_MEM_ENCRYPT:\narch/x86/coco/core.c:35:\tcase CC_ATTR_MEM_ENCRYPT:\narch/x86/coco/core.c-36-\t\treturn true;\n--\narch/x86/coco/core.c=47=static __maybe_unused __always_inline bool amd_cc_platform_vtom(enum cc_attr attr)\n--\narch/x86/coco/core.c-50-\tcase CC_ATTR_GUEST_MEM_ENCRYPT:\narch/x86/coco/core.c:51:\tcase CC_ATTR_MEM_ENCRYPT:\narch/x86/coco/core.c-52-\t\treturn true;\n--\narch/x86/coco/core.c-61- * cc_platform_has() function is used for this.  When a distinction isn't\narch/x86/coco/core.c:62: * needed, the CC_ATTR_MEM_ENCRYPT attribute can be used.\narch/x86/coco/core.c-63- *\n--\narch/x86/coco/core.c=70=static bool noinstr amd_cc_platform_has(enum cc_attr attr)\n--\narch/x86/coco/core.c-77-\tswitch (attr) {\narch/x86/coco/core.c:78:\tcase CC_ATTR_MEM_ENCRYPT:\narch/x86/coco/core.c-79-\t\treturn sme_me_mask;\n--\narch/x86/include/asm/vmware.h=228=unsigned long vmware_hypercall7(unsigned long cmd, unsigned long in1,\n--\narch/x86/include/asm/vmware.h-263- * High bandwidth calls are not supported on encrypted memory guests.\narch/x86/include/asm/vmware.h:264: * The caller should check cc_platform_has(CC_ATTR_MEM_ENCRYPT) and use\narch/x86/include/asm/vmware.h-265- * low bandwidth hypercall if memory encryption is set.\n--\narch/x86/mm/ioremap.c=729=bool arch_memremap_can_ram_remap(resource_size_t phys_addr, unsigned long size,\n--\narch/x86/mm/ioremap.c-731-{\narch/x86/mm/ioremap.c:732:\tif (!cc_platform_has(CC_ATTR_MEM_ENCRYPT))\narch/x86/mm/ioremap.c-733-\t\treturn true;\n--\narch/x86/mm/ioremap.c=756=pgprot_t __init early_memremap_pgprot_adjust(resource_size_t phys_addr,\n--\narch/x86/mm/ioremap.c-761-\narch/x86/mm/ioremap.c:762:\tif (!cc_platform_has(CC_ATTR_MEM_ENCRYPT))\narch/x86/mm/ioremap.c-763-\t\treturn prot;\n--\narch/x86/mm/mem_encrypt.c=89=void __init mem_encrypt_init(void)\narch/x86/mm/mem_encrypt.c-90-{\narch/x86/mm/mem_encrypt.c:91:\tif (!cc_platform_has(CC_ATTR_MEM_ENCRYPT))\narch/x86/mm/mem_encrypt.c-92-\t\treturn;\n--\narch/x86/mm/mem_encrypt_amd.c=547=void __init mem_encrypt_free_decrypted_mem(void)\n--\narch/x86/mm/mem_encrypt_amd.c-560-\t * sme_postprocess_startup(). Higher level abstractions, such as\narch/x86/mm/mem_encrypt_amd.c:561:\t * CC_ATTR_MEM_ENCRYPT, aren't necessarily equivalent in a Hyper-V VM\narch/x86/mm/mem_encrypt_amd.c-562-\t * using vTOM, where sme_me_mask is always zero.\n--\narch/x86/mm/pat/set_memory.c=2509=static int __set_memory_enc_dec(unsigned long addr, int numpages, bool enc)\n--\narch/x86/mm/pat/set_memory.c-2512-\narch/x86/mm/pat/set_memory.c:2513:\tif (cc_platform_has(CC_ATTR_MEM_ENCRYPT)) {\narch/x86/mm/pat/set_memory.c-2514-\t\tif (!down_read_trylock(\u0026mem_enc_lock))\n"
}

87/3 2026/09/26 15:33 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 51880
							output: 24
							thoughts: 290
						

Thoughts:
**Analyzing DMA and Confidential Computing Integration**

Okay, let me break this down. On x86, we're using `intel_cc_platform_has` to handle TDX, `amd_cc_platform_vtom` for Hyper-V vTOM, and `amd_cc_platform_has` for AMD SME/SEV/SNP. It looks like each of these uses their respective functions: `tdx_enc_status_change_prepare`, `hv_vtom_clear_present`, and `amd_enc_status_change_prepare` respectively for `enc_status_change_prepare`, and they all zero out memory using `memset` when encryption isn't enabled. That's the baseline.

Now, here's a potential issue that's got me thinking: what happens when `dma_set_decrypted` gets called, but `mark_mem_decrypt` is true? That could introduce a race condition or an unexpected state. This needs a deeper look.

Let's dive into the kernel code, specifically `kernel/dma/direct.c`. Right, here it is: `mark_mem_decrypt` is set based on these conditions:

```c
	if (force_dma_unencrypted(dev))
		attrs |= __DMA_ATTR_ALLOC_CC_SHARED;

	mark_mem_decrypt = attrs & __DMA_ATTR_ALLOC_CC_SHARED;
```

Ah, so `force_dma_unencrypted` comes into play. Wait a minute...can the *caller* directly pass in `__DMA_ATTR_ALLOC_CC_SHARED`? That could bypass our intended checks. I need to know where `__DMA_ATTR_ALLOC_CC_SHARED` is defined. Let me look that up real quick.



88/3 2026/09/26 15:33 tool grepper
0m Args:
{
  "Expression": "__DMA_ATTR_ALLOC_CC_SHARED"
}

Results:
{
  "Output": "Documentation/core-api/dma-attributes.rst=195=DMA_ATTR_MMIO is provided, the mapping requires a struct page.\nDocumentation/core-api/dma-attributes.rst-196-\nDocumentation/core-api/dma-attributes.rst:197:__DMA_ATTR_ALLOC_CC_SHARED\nDocumentation/core-api/dma-attributes.rst-198---------------------------\n--\nDocumentation/core-api/dma-attributes.rst=205=restore encryption on free.\nDocumentation/core-api/dma-attributes.rst-206-\nDocumentation/core-api/dma-attributes.rst:207:__DMA_ATTR_ALLOC_CC_SHARED differs from DMA_ATTR_CC_SHARED in that it is not\nDocumentation/core-api/dma-attributes.rst-208-a caller-visible DMA API attribute. DMA_ATTR_CC_SHARED describes an\n--\ndrivers/iommu/dma-iommu.c=1658=void *iommu_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,\n--\ndrivers/iommu/dma-iommu.c-1662-\tint ioprot = dma_info_to_prot(DMA_BIDIRECTIONAL, coherent, attrs);\ndrivers/iommu/dma-iommu.c:1663:\tbool is_alloc_cc_shared = attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED;\ndrivers/iommu/dma-iommu.c-1664-\tstruct page *page = NULL;\n--\ninclude/linux/dma-mapping.h-106-/*\ninclude/linux/dma-mapping.h:107: * __DMA_ATTR_ALLOC_CC_SHARED: Internal DMA-mapping attribute used by\ninclude/linux/dma-mapping.h-108- * allocation paths that create shared (decrypted) backing pages for\n--\ninclude/linux/dma-mapping.h-111- */\ninclude/linux/dma-mapping.h:112:#define __DMA_ATTR_ALLOC_CC_SHARED\t(1UL \u003c\u003c 14)\ninclude/linux/dma-mapping.h-113-\n--\ninclude/trace/events/dma.h=16=TRACE_DEFINE_ENUM(DMA_NONE);\n--\ninclude/trace/events/dma.h-38-\t\t{ DMA_ATTR_CC_SHARED, \"CC_SHARED\" }, \\\ninclude/trace/events/dma.h:39:\t\t{ __DMA_ATTR_ALLOC_CC_SHARED, \"ALLOC_CC_SHARED\" })\ninclude/trace/events/dma.h-40-\n--\nkernel/dma/contiguous.c=374=struct page *dma_alloc_from_contiguous(struct device *dev, size_t count,\n--\nkernel/dma/contiguous.c-378-\nkernel/dma/contiguous.c:379:\tif (attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED)\nkernel/dma/contiguous.c-380-\t\trequired_align = get_order(cc_shared_granule_size());\n--\nkernel/dma/contiguous.c=426=struct page *dma_alloc_contiguous(struct device *dev, size_t size,\n--\nkernel/dma/contiguous.c-437-\t\treturn NULL;\nkernel/dma/contiguous.c:438:\tif (attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED)\nkernel/dma/contiguous.c-439-\t\trequired_align = get_order(cc_shared_granule_size());\n--\nkernel/dma/direct.c=162=static struct page *dma_direct_alloc_from_pool(struct device *dev, size_t size,\n--\nkernel/dma/direct.c-177-\t*dma_handle = phys_to_dma_direct(dev, page_to_phys(page),\nkernel/dma/direct.c:178:\t\t\t\t\t attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED);\nkernel/dma/direct.c-179-\treturn page;\n--\nkernel/dma/direct.c=202=void *dma_direct_alloc(struct device *dev, size_t size,\n--\nkernel/dma/direct.c-212-\tif (force_dma_unencrypted(dev))\nkernel/dma/direct.c:213:\t\tattrs |= __DMA_ATTR_ALLOC_CC_SHARED;\nkernel/dma/direct.c-214-\nkernel/dma/direct.c:215:\tmark_mem_decrypt = attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED;\nkernel/dma/direct.c-216-\tif (mark_mem_decrypt) {\n--\nkernel/dma/direct.c-229-\nkernel/dma/direct.c:230:\tif (((attrs \u0026 (DMA_ATTR_NO_KERNEL_MAPPING | __DMA_ATTR_ALLOC_CC_SHARED)) ==\nkernel/dma/direct.c-231-\t     DMA_ATTR_NO_KERNEL_MAPPING) \u0026\u0026 !is_swiotlb_for_alloc(dev))\n--\nkernel/dma/direct.c-267-\t */\nkernel/dma/direct.c:268:\tif ((remap || (attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED)) \u0026\u0026\nkernel/dma/direct.c-269-\t    dma_direct_use_pool(dev, gfp)) {\n--\nkernel/dma/direct.c-349-\t*dma_handle = phys_to_dma_direct(dev, page_to_phys(page),\nkernel/dma/direct.c:350:\t\t\t\t\t attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED);\nkernel/dma/direct.c-351-\treturn cpu_addr;\n--\nkernel/dma/direct.c=369=void dma_direct_free(struct device *dev, size_t size,\n--\nkernel/dma/direct.c-382-\tif (force_dma_unencrypted(dev))\nkernel/dma/direct.c:383:\t\tattrs |= __DMA_ATTR_ALLOC_CC_SHARED;\nkernel/dma/direct.c-384-\nkernel/dma/direct.c:385:\tif (attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED)\nkernel/dma/direct.c-386-\t\tmark_mem_encrypted = true;\nkernel/dma/direct.c-387-\nkernel/dma/direct.c:388:\tif (((attrs \u0026 (DMA_ATTR_NO_KERNEL_MAPPING | __DMA_ATTR_ALLOC_CC_SHARED)) ==\nkernel/dma/direct.c-389-\t     DMA_ATTR_NO_KERNEL_MAPPING) \u0026\u0026 !is_swiotlb_for_alloc(dev)) {\n--\nkernel/dma/direct.c=447=struct page *dma_direct_alloc_pages(struct device *dev, size_t size,\n--\nkernel/dma/direct.c-456-\tif (force_dma_unencrypted(dev))\nkernel/dma/direct.c:457:\t\tattrs |= __DMA_ATTR_ALLOC_CC_SHARED;\nkernel/dma/direct.c-458-\nkernel/dma/direct.c:459:\tmark_mem_decrypt = attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED;\nkernel/dma/direct.c-460-\nkernel/dma/direct.c:461:\tif ((attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED) \u0026\u0026 dma_direct_use_pool(dev, gfp))\nkernel/dma/direct.c-462-\t\treturn dma_direct_alloc_from_pool(dev, size, dma_handle,\n--\nkernel/dma/direct.c-493-\t*dma_handle = phys_to_dma_direct(dev, page_to_phys(page),\nkernel/dma/direct.c:494:\t\t\t\t\t attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED);\nkernel/dma/direct.c-495-\treturn page;\n--\nkernel/dma/mapping.c=538=pgprot_t dma_pgprot(struct device *dev, pgprot_t prot, unsigned long attrs)\n--\nkernel/dma/mapping.c-550-\nkernel/dma/mapping.c:551:\tif (attrs \u0026 (DMA_ATTR_CC_SHARED | __DMA_ATTR_ALLOC_CC_SHARED))\nkernel/dma/mapping.c-552-\t\treturn pgprot_decrypted(dma_prot);\n--\nkernel/dma/mapping.c=633=void *dma_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle,\n--\nkernel/dma/mapping.c-648-\nkernel/dma/mapping.c:649:\tif (attrs \u0026 (DMA_ATTR_CC_SHARED | __DMA_ATTR_ALLOC_CC_SHARED)) {\nkernel/dma/mapping.c-650-\t\ttrace_dma_alloc(dev, NULL, 0, size, DMA_BIDIRECTIONAL, flag,\n--\nkernel/dma/mapping.c-655-\tif (force_dma_unencrypted(dev))\nkernel/dma/mapping.c:656:\t\tattrs |= __DMA_ATTR_ALLOC_CC_SHARED;\nkernel/dma/mapping.c-657-\n--\nkernel/dma/pool.c=85=static int atomic_pool_expand(struct dma_gen_pool *dma_pool, size_t pool_size,\n--\nkernel/dma/pool.c-105-\t\t\treturn -E2BIG;\nkernel/dma/pool.c:106:\t\tattrs = __DMA_ATTR_ALLOC_CC_SHARED;\nkernel/dma/pool.c-107-\t}\n--\nkernel/dma/pool.c=335=struct page *dma_alloc_from_pool(struct device *dev, size_t size,\n--\nkernel/dma/pool.c-344-\nkernel/dma/pool.c:345:\t\tif (dma_pool-\u003ecc_shared != !!(attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED))\nkernel/dma/pool.c-346-\t\t\tcontinue;\n--\nkernel/dma/swiotlb.c=724=static struct page *alloc_dma_pages(gfp_t gfp, size_t bytes,\n--\nkernel/dma/swiotlb.c-726-{\nkernel/dma/swiotlb.c:727:\tbool cc_shared = attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED;\nkernel/dma/swiotlb.c-728-\tunsigned int order;\n--\nkernel/dma/swiotlb.c=769=static struct page *swiotlb_alloc_tlb(struct device *dev,\n--\nkernel/dma/swiotlb.c-773-\tstruct page *page;\nkernel/dma/swiotlb.c:774:\tunsigned long attrs = mem-\u003ecc_shared ? __DMA_ATTR_ALLOC_CC_SHARED : 0;\nkernel/dma/swiotlb.c-775-\n--\nkernel/dma/swiotlb.c=1965=struct page *swiotlb_alloc(struct device *dev, size_t size, unsigned long attrs)\n--\nkernel/dma/swiotlb.c-1975-\nkernel/dma/swiotlb.c:1976:\tif (mem-\u003ecc_shared != !!(attrs \u0026 __DMA_ATTR_ALLOC_CC_SHARED))\nkernel/dma/swiotlb.c-1977-\t\treturn NULL;\n"
}

89/3 2026/09/26 15:34 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 55552
							output: 36
							thoughts: 317
						

Thoughts:
**Analyzing DMA Page Allocation in the Kernel**

Okay, let's dive into `alloc_dma_pages` within `kernel/dma/swiotlb.c`. My initial thought is to understand its workings. I can see the core logic: the function allocates physical pages, checks for a physical address limit, and potentially deals with shared memory concepts, judging by the `cc_shared` attribute. It's a fairly standard page allocation routine, but I need to understand its context.

Specifically, I'm noting the `gfp` flags, the bytes requested, the physical limit, and those allocation attributes. It looks like it handles alignment for shared cache coherence, that's interesting.

Now, a critical question arises: *who* is calling this function? To fully grasp its purpose and potential bottlenecks, I need to see the call chain. I'll need to immediately investigate the callers of `alloc_dma_pages` within the same file. That will illuminate the scenarios this allocation routine is designed for. Understanding the context of this allocation is key to optimization or debugging.



90/3 2026/09/26 15:34 tool grepper
0m Args:
{
  "Expression": "alloc_dma_pages",
  "PathPrefix": "kernel/dma/swiotlb.c"
}

Results:
{
  "Output": "kernel/dma/swiotlb.c=667=void __init swiotlb_exit(void)\n--\nkernel/dma/swiotlb.c-711-/**\nkernel/dma/swiotlb.c:712: * alloc_dma_pages() - allocate pages to be used for DMA\nkernel/dma/swiotlb.c-713- * @gfp:\tGFP flags for the allocation.\n--\nkernel/dma/swiotlb.c-723- */\nkernel/dma/swiotlb.c:724:static struct page *alloc_dma_pages(gfp_t gfp, size_t bytes,\nkernel/dma/swiotlb.c-725-\t\tu64 phys_limit, unsigned long attrs)\n--\nkernel/dma/swiotlb.c=769=static struct page *swiotlb_alloc_tlb(struct device *dev,\n--\nkernel/dma/swiotlb.c-795-\nkernel/dma/swiotlb.c:796:\twhile (IS_ERR(page = alloc_dma_pages(gfp, bytes, phys_limit, attrs))) {\nkernel/dma/swiotlb.c-797-\t\tif (IS_ENABLED(CONFIG_ZONE_DMA32) \u0026\u0026\n"
}

91/3 2026/09/26 15:34 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 56467
							output: 28
							thoughts: 569
						

Thoughts:
**Analyzing Memory Allocation and Encryption in Hyper-V**

Okay, let's dive into this. First, I'm thinking about how `swiotlb_alloc_tlb` allocates memory blocks. It's a fundamental part of the I/O translation layer, I'm already pretty familiar with that.

Now, I need to understand how Hyper-V handles memory allocation, specifically in relation to encryption. I'm focusing on `drivers/hv/connection.c`, `drivers/hv/hv.c`, and `drivers/hv/hv_common.c`. Let's zero in on `drivers/hv/hv.c`.

Aha, I see a code change in `hv_alloc_page`. The original code had the `set_memory_decrypted` call followed by a `memset` regardless. Now, it's been wrapped in an `if (decrypt)` block. This makes me consider a critical point: if `decrypt` is true, does `set_memory_decrypted` *also* zero out the memory? I'd need to dig deeper into that function. The intention of this change is clearly about performance.

Then I start thinking about the implications. What happens if Hyper-V is running on an AMD SEV, Intel TDX, or even the Hyper-V IVM? If that's the case, then either `amd_enc_status_change_prepare`, `tdx_enc_status_change_prepare`, or `hv_vtom_clear_present` might zero the memory, negating the need for the `memset`. That's where I need to focus my attention. It's security-sensitive to get the memory clearing right.

But what if Hyper-V is *not* running in a confidential computing environment? In a standard guest setup, when does `hv_alloc_page` actually get called with `decrypt = true`? I need to analyze the callers of `hv_alloc_page` within the `drivers/hv/` directory to understand the context and purpose of the encryption. That's the next step: follow the call chain and understand when this encryption flag is actually set.



92/3 2026/09/26 15:34 tool grepper
0m Args:
{
  "Expression": "hv_alloc_page",
  "PathPrefix": "drivers/hv"
}

Results:
{
  "Output": "drivers/hv/hv.c=109=EXPORT_SYMBOL_FOR_MODULES(hv_post_message, \"mshv_vtl\");\ndrivers/hv/hv.c-110-\ndrivers/hv/hv.c:111:static int hv_alloc_page(void **page, bool decrypt, const char *note)\ndrivers/hv/hv.c-112-{\n--\ndrivers/hv/hv.c=171=int hv_synic_alloc(void)\n--\ndrivers/hv/hv.c-199-\t\tif (ms_hyperv.paravisor_present \u0026\u0026 hv_isolation_type_tdx()) {\ndrivers/hv/hv.c:200:\t\t\tret = hv_alloc_page(\u0026hv_cpu-\u003epost_msg_page,\ndrivers/hv/hv.c-201-\t\t\t\tdecrypt, \"post msg\");\n--\ndrivers/hv/hv.c-211-\t\tif (!ms_hyperv.paravisor_present \u0026\u0026 !hv_root_partition()) {\ndrivers/hv/hv.c:212:\t\t\tret = hv_alloc_page(\u0026hv_cpu-\u003ehyp_synic_message_page,\ndrivers/hv/hv.c-213-\t\t\t\tdecrypt, \"hypervisor SynIC msg\");\n--\ndrivers/hv/hv.c-215-\t\t\t\tgoto err;\ndrivers/hv/hv.c:216:\t\t\tret = hv_alloc_page(\u0026hv_cpu-\u003ehyp_synic_event_page,\ndrivers/hv/hv.c-217-\t\t\t\tdecrypt, \"hypervisor SynIC event\");\n--\ndrivers/hv/hv.c-222-\t\tif (vmbus_is_confidential()) {\ndrivers/hv/hv.c:223:\t\t\tret = hv_alloc_page(\u0026hv_cpu-\u003epara_synic_message_page,\ndrivers/hv/hv.c-224-\t\t\t\tfalse, \"paravisor SynIC msg\");\n--\ndrivers/hv/hv.c-226-\t\t\t\tgoto err;\ndrivers/hv/hv.c:227:\t\t\tret = hv_alloc_page(\u0026hv_cpu-\u003epara_synic_event_page,\ndrivers/hv/hv.c-228-\t\t\t\tfalse, \"paravisor SynIC event\");\n"
}

93/3 2026/09/26 15:34 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 58084
							output: 35
							thoughts: 19
						

Thoughts:
Okay, here's my attempt at summarizing that thought process, keeping in mind the expert user:

**Deep Dive into `hv_synic_alloc` - Time to Trace `decrypt`**

Alright, let's get down to brass tacks. I'm taking a look at `hv_synic_alloc`, and my immediate thought is: where does the `decrypt` variable fit into this picture? I know this function is crucial for the Hyper-V synthetic NIC allocation process, so understanding the role of `decrypt` is key to understanding the overall flow and potential security implications (or lack thereof) of the allocation. Time to dig in and see how it's being used, and what context is given with this variable!



94/3 2026/09/26 15:34 tool read-file
0m Args:
{
  "File": "drivers/hv/hv.c",
  "FirstLine": 170,
  "LineCount": 40
}

Results:
{
  "Contents": " 170:\t\n 171:\tint hv_synic_alloc(void)\n 172:\t{\n 173:\t\tint cpu, ret = -ENOMEM;\n 174:\t\tstruct hv_per_cpu_context *hv_cpu;\n 175:\t\tconst bool decrypt = !vmbus_is_confidential();\n 176:\t\n 177:\t\t/*\n 178:\t\t * First, zero all per-cpu memory areas so hv_synic_free() can\n 179:\t\t * detect what memory has been allocated and cleanup properly\n 180:\t\t * after any failures.\n 181:\t\t */\n 182:\t\tfor_each_present_cpu(cpu) {\n 183:\t\t\thv_cpu = per_cpu_ptr(hv_context.cpu_context, cpu);\n 184:\t\t\tmemset(hv_cpu, 0, sizeof(*hv_cpu));\n 185:\t\t}\n 186:\t\n 187:\t\thv_context.hv_numa_map = kzalloc_objs(struct cpumask, nr_node_ids);\n 188:\t\tif (!hv_context.hv_numa_map) {\n 189:\t\t\tpr_err(\"Unable to allocate NUMA map\\n\");\n 190:\t\t\tgoto err;\n 191:\t\t}\n 192:\t\n 193:\t\tfor_each_present_cpu(cpu) {\n 194:\t\t\thv_cpu = per_cpu_ptr(hv_context.cpu_context, cpu);\n 195:\t\n 196:\t\t\ttasklet_init(\u0026hv_cpu-\u003emsg_dpc,\n 197:\t\t\t\t     vmbus_on_msg_dpc, (unsigned long)hv_cpu);\n 198:\t\n 199:\t\t\tif (ms_hyperv.paravisor_present \u0026\u0026 hv_isolation_type_tdx()) {\n 200:\t\t\t\tret = hv_alloc_page(\u0026hv_cpu-\u003epost_msg_page,\n 201:\t\t\t\t\tdecrypt, \"post msg\");\n 202:\t\t\t\tif (ret)\n 203:\t\t\t\t\tgoto err;\n 204:\t\t\t}\n 205:\t\n 206:\t\t\t/*\n 207:\t\t\t * If these SynIC pages are not allocated, SIEF and SIM pages\n 208:\t\t\t * are configured using what the root partition or the paravisor\n 209:\t\t\t * provides upon reading the SIEFP and SIMP registers.\n"
}

95/3 2026/09/26 15:34 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 58935
							output: 22
							thoughts: 59
						

Thoughts:
Okay, let's see. My eyes just snagged on line 175 of this code... specifically this line: `const bool decrypt = !vmbus_is_confidential();`. Hold on a second, that's interesting. `vmbus_is_confidential()`... hmm, I've seen that function name before, or something like it, but I can't quite place it. A quick look at the logic, and it seems the *opposite* of that function's return value determines if we decrypt something. I need to understand that function better to be certain I'm interpreting this correctly, so I'm going to search for the definition of `vmbus_is_confidential()` right away. Let's see what that function does and then this whole line will make more sense.



96/3 2026/09/26 15:34 tool grepper
0m Args:
{
  "Expression": "vmbus_is_confidential"
}

Results:
{
  "Output": "drivers/hv/channel_mgmt.c=1027=static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)\n--\ndrivers/hv/channel_mgmt.c-1052-\tif (co_ring_buffer || co_external_memory) {\ndrivers/hv/channel_mgmt.c:1053:\t\tif (vmbus_proto_version \u003c VERSION_WIN10_V6_0 || !vmbus_is_confidential()) {\ndrivers/hv/channel_mgmt.c-1054-\t\t\tpr_err(\"Invalid offer relid=%d: no support for confidential VMBus\\n\",\n--\ndrivers/hv/connection.c=76=static int vmbus_try_connection_id(struct vmbus_channel_msginfo *msginfo,\n--\ndrivers/hv/connection.c-110-\ndrivers/hv/connection.c:111:\tif (vmbus_is_confidential() \u0026\u0026 version \u003e= VERSION_WIN10_V6_0)\ndrivers/hv/connection.c-112-\t\tmsg-\u003efeature_flags = VMBUS_FEATURE_FLAG_CONFIDENTIAL_CHANNELS;\n--\ndrivers/hv/hv.c=49=int hv_post_message(union hv_connection_id connection_id,\n--\ndrivers/hv/hv.c-78-\ndrivers/hv/hv.c:79:\tif (ms_hyperv.paravisor_present \u0026\u0026 !vmbus_is_confidential()) {\ndrivers/hv/hv.c-80-\t\t/*\n--\ndrivers/hv/hv.c=171=int hv_synic_alloc(void)\n--\ndrivers/hv/hv.c-174-\tstruct hv_per_cpu_context *hv_cpu;\ndrivers/hv/hv.c:175:\tconst bool decrypt = !vmbus_is_confidential();\ndrivers/hv/hv.c-176-\n--\ndrivers/hv/hv.c-221-\ndrivers/hv/hv.c:222:\t\tif (vmbus_is_confidential()) {\ndrivers/hv/hv.c-223-\t\t\tret = hv_alloc_page(\u0026hv_cpu-\u003epara_synic_message_page,\n--\ndrivers/hv/hv.c=244=void hv_synic_free(void)\n--\ndrivers/hv/hv.c-246-\tint cpu;\ndrivers/hv/hv.c:247:\tconst bool encrypt = !vmbus_is_confidential();\ndrivers/hv/hv.c-248-\n--\ndrivers/hv/hv.c-261-\t\t}\ndrivers/hv/hv.c:262:\t\tif (vmbus_is_confidential()) {\ndrivers/hv/hv.c-263-\t\t\thv_free_page(\u0026hv_cpu-\u003epara_synic_event_page,\n--\ndrivers/hv/hv.c=381=int hv_synic_init(unsigned int cpu)\ndrivers/hv/hv.c-382-{\ndrivers/hv/hv.c:383:\tif (vmbus_is_confidential())\ndrivers/hv/hv.c-384-\t\thv_para_synic_enable_regs(cpu);\n--\ndrivers/hv/hv.c-397-\thv_hyp_synic_enable_regs(cpu);\ndrivers/hv/hv.c:398:\tif (vmbus_is_confidential())\ndrivers/hv/hv.c-399-\t\thv_para_synic_enable_interrupts();\n--\ndrivers/hv/hv.c=579=int hv_synic_cleanup(unsigned int cpu)\n--\ndrivers/hv/hv.c-638-\thv_hyp_synic_disable_regs(cpu);\ndrivers/hv/hv.c:639:\tif (!vmbus_is_confidential())\ndrivers/hv/hv.c-640-\t\thv_hyp_synic_disable_interrupts();\n--\ndrivers/hv/hv.c-646-\t */\ndrivers/hv/hv.c:647:\tif (vmbus_is_confidential()) {\ndrivers/hv/hv.c-648-\t\thv_para_synic_disable_regs(cpu);\n--\ndrivers/hv/hyperv_vmbus.h=354=extern const struct vmbus_channel_message_table_entry\n--\ndrivers/hv/hyperv_vmbus.h-359-\ndrivers/hv/hyperv_vmbus.h:360:bool vmbus_is_confidential(void);\ndrivers/hv/hyperv_vmbus.h-361-\n--\ndrivers/hv/hyperv_vmbus.h=364=static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type)\n--\ndrivers/hv/hyperv_vmbus.h-393-\t\t */\ndrivers/hv/hyperv_vmbus.h:394:\t\tif (vmbus_is_confidential())\ndrivers/hv/hyperv_vmbus.h-395-\t\t\thv_para_set_synic_register(HV_MSR_EOM, 0);\n--\ndrivers/hv/vmbus_drv.c=64=static bool is_confidential;\ndrivers/hv/vmbus_drv.c-65-\ndrivers/hv/vmbus_drv.c:66:bool vmbus_is_confidential(void)\ndrivers/hv/vmbus_drv.c-67-{\n--\ndrivers/hv/vmbus_drv.c-69-}\ndrivers/hv/vmbus_drv.c:70:EXPORT_SYMBOL_GPL(vmbus_is_confidential);\ndrivers/hv/vmbus_drv.c-71-\n"
}

97/3 2026/09/26 15:34 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 60661
							output: 38
							thoughts: 20
						

Thoughts:
Okay, here's my interpretation of what's going on, thinking as if these are *my* thoughts:

**Peering into the Hyper-V VMBus Driver**

Alright, time to dig into this VMBus driver code... Let's head straight to `drivers/hv/vmbus_drv.c:64`. That's likely the heart of the initialization or something critical for the overall driver's functionality. I'm hoping I can quickly pinpoint the entry point or understand how this driver hooks into the kernel's overall lifecycle for Hyper-V communication. I need to refresh my memory about the specifics of the VMBus architecture... I expect to find some key function calls setting up the channel, handling interrupts, or registering with the underlying hardware, or, at a minimum, setting up the framework for the driver to interact with the host system. It is also possible that a probe happens here.



98/3 2026/09/26 15:34 tool read-file
0m Args:
{
  "File": "drivers/hv/vmbus_drv.c",
  "FirstLine": 60,
  "LineCount": 25
}

Results:
{
  "Contents": "  60:\t/*\n  61:\t * If the Confidential VMBus is used, the data on the \"wire\" is not\n  62:\t * visible to either the host or the hypervisor.\n  63:\t */\n  64:\tstatic bool is_confidential;\n  65:\t\n  66:\tbool vmbus_is_confidential(void)\n  67:\t{\n  68:\t\treturn is_confidential;\n  69:\t}\n  70:\tEXPORT_SYMBOL_GPL(vmbus_is_confidential);\n  71:\t\n  72:\tstatic bool skip_vmbus_unload;\n  73:\t\n  74:\t/*\n  75:\t * Allow a VMBus framebuffer driver to specify that in the case of a panic,\n  76:\t * it will do the VMbus unload operation once it has flushed any dirty\n  77:\t * portions of the framebuffer to the Hyper-V host.\n  78:\t */\n  79:\tvoid vmbus_set_skip_unload(bool skip)\n  80:\t{\n  81:\t\tskip_vmbus_unload = skip;\n  82:\t}\n  83:\tEXPORT_SYMBOL_GPL(vmbus_set_skip_unload);\n  84:\t\n"
}

99/3 2026/09/26 15:34 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 61251
							output: 27
							thoughts: 41
						

Thoughts:
Okay, let's see. My mind's racing... **Analyzing `is_confidential` in `drivers/hv/vmbus_drv.c`...**

Hang on a second! I need to pinpoint the use of `is_confidential`. When does this flag actually get set? This is crucial to understanding the security implications here, or the data flow. Gotta figure out the context in which that's happening, what's triggering the setting of that particular flag! Alright, let's trace this and see what we can find.



100/3 2026/09/26 15:34 tool grepper
0m Args:
{
  "Expression": "is_confidential",
  "PathPrefix": "drivers/hv"
}

Results:
{
  "Output": "drivers/hv/channel_mgmt.c=1027=static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)\n--\ndrivers/hv/channel_mgmt.c-1052-\tif (co_ring_buffer || co_external_memory) {\ndrivers/hv/channel_mgmt.c:1053:\t\tif (vmbus_proto_version \u003c VERSION_WIN10_V6_0 || !vmbus_is_confidential()) {\ndrivers/hv/channel_mgmt.c-1054-\t\t\tpr_err(\"Invalid offer relid=%d: no support for confidential VMBus\\n\",\n--\ndrivers/hv/connection.c=76=static int vmbus_try_connection_id(struct vmbus_channel_msginfo *msginfo,\n--\ndrivers/hv/connection.c-110-\ndrivers/hv/connection.c:111:\tif (vmbus_is_confidential() \u0026\u0026 version \u003e= VERSION_WIN10_V6_0)\ndrivers/hv/connection.c-112-\t\tmsg-\u003efeature_flags = VMBUS_FEATURE_FLAG_CONFIDENTIAL_CHANNELS;\n--\ndrivers/hv/hv.c=49=int hv_post_message(union hv_connection_id connection_id,\n--\ndrivers/hv/hv.c-78-\ndrivers/hv/hv.c:79:\tif (ms_hyperv.paravisor_present \u0026\u0026 !vmbus_is_confidential()) {\ndrivers/hv/hv.c-80-\t\t/*\n--\ndrivers/hv/hv.c=171=int hv_synic_alloc(void)\n--\ndrivers/hv/hv.c-174-\tstruct hv_per_cpu_context *hv_cpu;\ndrivers/hv/hv.c:175:\tconst bool decrypt = !vmbus_is_confidential();\ndrivers/hv/hv.c-176-\n--\ndrivers/hv/hv.c-221-\ndrivers/hv/hv.c:222:\t\tif (vmbus_is_confidential()) {\ndrivers/hv/hv.c-223-\t\t\tret = hv_alloc_page(\u0026hv_cpu-\u003epara_synic_message_page,\n--\ndrivers/hv/hv.c=244=void hv_synic_free(void)\n--\ndrivers/hv/hv.c-246-\tint cpu;\ndrivers/hv/hv.c:247:\tconst bool encrypt = !vmbus_is_confidential();\ndrivers/hv/hv.c-248-\n--\ndrivers/hv/hv.c-261-\t\t}\ndrivers/hv/hv.c:262:\t\tif (vmbus_is_confidential()) {\ndrivers/hv/hv.c-263-\t\t\thv_free_page(\u0026hv_cpu-\u003epara_synic_event_page,\n--\ndrivers/hv/hv.c=381=int hv_synic_init(unsigned int cpu)\ndrivers/hv/hv.c-382-{\ndrivers/hv/hv.c:383:\tif (vmbus_is_confidential())\ndrivers/hv/hv.c-384-\t\thv_para_synic_enable_regs(cpu);\n--\ndrivers/hv/hv.c-397-\thv_hyp_synic_enable_regs(cpu);\ndrivers/hv/hv.c:398:\tif (vmbus_is_confidential())\ndrivers/hv/hv.c-399-\t\thv_para_synic_enable_interrupts();\n--\ndrivers/hv/hv.c=579=int hv_synic_cleanup(unsigned int cpu)\n--\ndrivers/hv/hv.c-638-\thv_hyp_synic_disable_regs(cpu);\ndrivers/hv/hv.c:639:\tif (!vmbus_is_confidential())\ndrivers/hv/hv.c-640-\t\thv_hyp_synic_disable_interrupts();\n--\ndrivers/hv/hv.c-646-\t */\ndrivers/hv/hv.c:647:\tif (vmbus_is_confidential()) {\ndrivers/hv/hv.c-648-\t\thv_para_synic_disable_regs(cpu);\n--\ndrivers/hv/hyperv_vmbus.h=354=extern const struct vmbus_channel_message_table_entry\n--\ndrivers/hv/hyperv_vmbus.h-359-\ndrivers/hv/hyperv_vmbus.h:360:bool vmbus_is_confidential(void);\ndrivers/hv/hyperv_vmbus.h-361-\n--\ndrivers/hv/hyperv_vmbus.h=364=static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type)\n--\ndrivers/hv/hyperv_vmbus.h-393-\t\t */\ndrivers/hv/hyperv_vmbus.h:394:\t\tif (vmbus_is_confidential())\ndrivers/hv/hyperv_vmbus.h-395-\t\t\thv_para_set_synic_register(HV_MSR_EOM, 0);\n--\ndrivers/hv/vmbus_drv.c=58=int vmbus_interrupt;\n--\ndrivers/hv/vmbus_drv.c-63- */\ndrivers/hv/vmbus_drv.c:64:static bool is_confidential;\ndrivers/hv/vmbus_drv.c-65-\ndrivers/hv/vmbus_drv.c:66:bool vmbus_is_confidential(void)\ndrivers/hv/vmbus_drv.c-67-{\ndrivers/hv/vmbus_drv.c:68:\treturn is_confidential;\ndrivers/hv/vmbus_drv.c-69-}\ndrivers/hv/vmbus_drv.c:70:EXPORT_SYMBOL_GPL(vmbus_is_confidential);\ndrivers/hv/vmbus_drv.c-71-\n--\ndrivers/hv/vmbus_drv.c=1469=static int vmbus_bus_init(void)\n--\ndrivers/hv/vmbus_drv.c-1513-\t */\ndrivers/hv/vmbus_drv.c:1514:\tis_confidential = ms_hyperv.confidential_vmbus_available;\ndrivers/hv/vmbus_drv.c:1515:\tif (is_confidential)\ndrivers/hv/vmbus_drv.c-1516-\t\tpr_info(\"Establishing connection to the confidential VMBus\\n\");\ndrivers/hv/vmbus_drv.c:1517:\thv_para_set_sint_proxy(!is_confidential);\ndrivers/hv/vmbus_drv.c-1518-\tret = vmbus_alloc_synic_and_connect();\n"
}

101/3 2026/09/26 15:34 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 63190
							output: 32
							thoughts: 1936
						

Thoughts:
**A Deep Dive into Hyper-V Memory Handling and a Potential Vulnerability**

Okay, let's break this down. My initial observation is focused on how memory is allocated and handled within Hyper-V, specifically concerning confidential VMs versus standard VMs. I started by looking at `hv_synic_alloc` and the use of the `decrypt` flag. The crucial point is that in a non-confidential VM, `decrypt` is set to TRUE. This is because `decrypt` is the *inverse* of `vmbus_is_confidential()`.

Now, the code calls `hv_alloc_page`, passing `decrypt = true`. Inside `hv_alloc_page`, a page is allocated. Critically, if `decrypt` is true, the code calls `set_memory_decrypted()`. The problem is that in a *non-confidential* VM, `set_memory_decrypted()` *does nothing* (because `cc_platform_has(CC_ATTR_MEM_ENCRYPT)` is false) and then, unlike the previous version, the new code *skips the zeroing* step (the `memset` call is in the `else` branch). This means that a newly allocated page is *not* zeroed.

I need to confirm this understanding, because the implications are significant. If this is true, a page is being allocated, but not initialized to zero when CC is not being used. It is being allocated from the kernel memory pool, and it might contain sensitive information from previous uses. This is a potential information leak vulnerability.

To solidify this suspicion, I looked at other parts of the code. In `drivers/hv/connection.c` and `arch/x86/kernel/kvmclock.c`, I noticed they *explicitly* check for `cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)` before calling `set_memory_decrypted` or `memset`. In these examples, the developers are making an effort to conditionally zero pages in the absence of CC encryption. This reinforces my belief that the lack of zeroing in the `hv_alloc_page` function is an oversight and a regression. The previous version of the code *always* zeroed the page.

Looking back to `drivers/hv/hv.c`, I see the same pattern of `if (decrypt) set_memory_decrypted() else memset()` which suggests that the intent was to conditionally zero the page. Here is the clue:  The developer *assumed* that the `decrypt` flag directly corresponded to CC encryption. However, `decrypt` in `hv_synic_alloc` is the negation of `vmbus_is_confidential()`. The assumption that this is the same as encryption is incorrect, as it also refers to the standard Hyper-V VMs. This patch has introduced a security issue.

My next step is to examine the commit history and accompanying comments to understand the original intent and the reasons for this change. This looks like a classic example of an unintended consequence of a code refactor.



102/3 2026/09/26 15:34 tool grepper
0m Args:
{
  "Expression": "!vmbus_is_confidential",
  "PathPrefix": "drivers/hv"
}

Results:
{
  "Output": "drivers/hv/channel_mgmt.c=1027=static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)\n--\ndrivers/hv/channel_mgmt.c-1052-\tif (co_ring_buffer || co_external_memory) {\ndrivers/hv/channel_mgmt.c:1053:\t\tif (vmbus_proto_version \u003c VERSION_WIN10_V6_0 || !vmbus_is_confidential()) {\ndrivers/hv/channel_mgmt.c-1054-\t\t\tpr_err(\"Invalid offer relid=%d: no support for confidential VMBus\\n\",\n--\ndrivers/hv/hv.c=49=int hv_post_message(union hv_connection_id connection_id,\n--\ndrivers/hv/hv.c-78-\ndrivers/hv/hv.c:79:\tif (ms_hyperv.paravisor_present \u0026\u0026 !vmbus_is_confidential()) {\ndrivers/hv/hv.c-80-\t\t/*\n--\ndrivers/hv/hv.c=171=int hv_synic_alloc(void)\n--\ndrivers/hv/hv.c-174-\tstruct hv_per_cpu_context *hv_cpu;\ndrivers/hv/hv.c:175:\tconst bool decrypt = !vmbus_is_confidential();\ndrivers/hv/hv.c-176-\n--\ndrivers/hv/hv.c=244=void hv_synic_free(void)\n--\ndrivers/hv/hv.c-246-\tint cpu;\ndrivers/hv/hv.c:247:\tconst bool encrypt = !vmbus_is_confidential();\ndrivers/hv/hv.c-248-\n--\ndrivers/hv/hv.c=579=int hv_synic_cleanup(unsigned int cpu)\n--\ndrivers/hv/hv.c-638-\thv_hyp_synic_disable_regs(cpu);\ndrivers/hv/hv.c:639:\tif (!vmbus_is_confidential())\ndrivers/hv/hv.c-640-\t\thv_hyp_synic_disable_interrupts();\n"
}

103/3 2026/09/26 15:34 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 66361
							output: 35
							thoughts: 55
						

Thoughts:
Okay, let's dive into this. I need to refresh my memory on the Hyper-V SynIC allocation and initialization routines. My starting point is going to be `drivers/hv/hv.c`, specifically focusing on `hv_synic_alloc` and `hv_synic_init`. I know these are crucial for setting up the interrupt controller within the Hyper-V environment, and I suspect there might be nuances or optimizations that I haven't fully considered recently.

I'm zeroing in on lines 205-245; that's probably where the heart of the initialization and allocation logic resides. I'll be looking for how they handle memory mapping, interrupt vector assignment, and any synchronization primitives employed. My goal is to quickly assess the code's efficiency, understand how it interacts with the underlying hardware, and identify any potential areas for improvement or, perhaps, lurking vulnerabilities. I need to keep an eye out for potential race conditions or resource contention issues, especially during initialization. Let's see what we've got.



104/3 2026/09/26 15:34 tool read-file
0m Args:
{
  "File": "drivers/hv/hv.c",
  "FirstLine": 205,
  "LineCount": 40
}

Results:
{
  "Contents": " 205:\t\n 206:\t\t\t/*\n 207:\t\t\t * If these SynIC pages are not allocated, SIEF and SIM pages\n 208:\t\t\t * are configured using what the root partition or the paravisor\n 209:\t\t\t * provides upon reading the SIEFP and SIMP registers.\n 210:\t\t\t */\n 211:\t\t\tif (!ms_hyperv.paravisor_present \u0026\u0026 !hv_root_partition()) {\n 212:\t\t\t\tret = hv_alloc_page(\u0026hv_cpu-\u003ehyp_synic_message_page,\n 213:\t\t\t\t\tdecrypt, \"hypervisor SynIC msg\");\n 214:\t\t\t\tif (ret)\n 215:\t\t\t\t\tgoto err;\n 216:\t\t\t\tret = hv_alloc_page(\u0026hv_cpu-\u003ehyp_synic_event_page,\n 217:\t\t\t\t\tdecrypt, \"hypervisor SynIC event\");\n 218:\t\t\t\tif (ret)\n 219:\t\t\t\t\tgoto err;\n 220:\t\t\t}\n 221:\t\n 222:\t\t\tif (vmbus_is_confidential()) {\n 223:\t\t\t\tret = hv_alloc_page(\u0026hv_cpu-\u003epara_synic_message_page,\n 224:\t\t\t\t\tfalse, \"paravisor SynIC msg\");\n 225:\t\t\t\tif (ret)\n 226:\t\t\t\t\tgoto err;\n 227:\t\t\t\tret = hv_alloc_page(\u0026hv_cpu-\u003epara_synic_event_page,\n 228:\t\t\t\t\tfalse, \"paravisor SynIC event\");\n 229:\t\t\t\tif (ret)\n 230:\t\t\t\t\tgoto err;\n 231:\t\t\t}\n 232:\t\t}\n 233:\t\n 234:\t\treturn 0;\n 235:\t\n 236:\terr:\n 237:\t\t/*\n 238:\t\t * Any memory allocations that succeeded will be freed when\n 239:\t\t * the caller cleans up by calling hv_synic_free()\n 240:\t\t */\n 241:\t\treturn ret;\n 242:\t}\n 243:\t\n 244:\tvoid hv_synic_free(void)\n"
}

105/3 2026/09/26 15:34 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 67238
							output: 31
							thoughts: 206
						

Thoughts:
**Analyzing Hyper-V SynIC Message and Event Pages**

Okay, let's zoom in on lines 211-220 of this code snippet. Specifically, I see this conditional block:

```c
			if (!ms_hyperv.paravisor_present && !hv_root_partition()) {
				ret = hv_alloc_page(&hv_cpu->hyp_synic_message_page,
					decrypt, "hypervisor SynIC msg");
				if (ret)
					goto err;
				ret = hv_alloc_page(&hv_cpu->hyp_synic_event_page,
					decrypt, "hypervisor SynIC event");
				if (ret)
					goto err;
			}
```

I'm immediately intrigued by `hyp_synic_message_page` and `hyp_synic_event_page`. These must be crucial for Hyper-V's SynIC (Synthetic Interrupt Controller) functionality. Since I'm very familiar with the Hyper-V architecture, I know these probably deal with the message and event queues used for inter-VM communication and hypervisor interactions. But it's time to refresh my memory and understand how the code handles these pages.

Let's trace how the code sets up and manages these `hyp_synic_message_page` and `hyp_synic_event_page` variables. I'll need to figure out what context they are used within. Let me perform a code search to see where else `hyp_synic_message_page` is referenced.



106/3 2026/09/26 15:34 tool grepper
0m Args:
{
  "Expression": "hyp_synic_message_page",
  "PathPrefix": "drivers/hv"
}

Results:
{
  "Output": "drivers/hv/channel_mgmt.c=821=static void vmbus_wait_for_unload(void)\n--\ndrivers/hv/channel_mgmt.c-854-\t\t\t/*\ndrivers/hv/channel_mgmt.c:855:\t\t\t * In a CoCo VM the hyp_synic_message_page is not allocated\ndrivers/hv/channel_mgmt.c-856-\t\t\t * in hv_synic_alloc(). Instead it is set/cleared in\n--\ndrivers/hv/channel_mgmt.c-861-\t\t\t */\ndrivers/hv/channel_mgmt.c:862:\t\t\tpage_addr = hv_cpu-\u003ehyp_synic_message_page;\ndrivers/hv/channel_mgmt.c-863-\t\t\tif (!page_addr)\n--\ndrivers/hv/channel_mgmt.c-902-\ndrivers/hv/channel_mgmt.c:903:\t\tpage_addr = hv_cpu-\u003ehyp_synic_message_page;\ndrivers/hv/channel_mgmt.c-904-\t\tif (!page_addr)\n--\ndrivers/hv/hv.c=171=int hv_synic_alloc(void)\n--\ndrivers/hv/hv.c-211-\t\tif (!ms_hyperv.paravisor_present \u0026\u0026 !hv_root_partition()) {\ndrivers/hv/hv.c:212:\t\t\tret = hv_alloc_page(\u0026hv_cpu-\u003ehyp_synic_message_page,\ndrivers/hv/hv.c-213-\t\t\t\tdecrypt, \"hypervisor SynIC msg\");\n--\ndrivers/hv/hv.c=244=void hv_synic_free(void)\n--\ndrivers/hv/hv.c-258-\t\t\t\tencrypt, \"hypervisor SynIC event\");\ndrivers/hv/hv.c:259:\t\t\thv_free_page(\u0026hv_cpu-\u003ehyp_synic_message_page,\ndrivers/hv/hv.c-260-\t\t\t\tencrypt, \"hypervisor SynIC msg\");\n--\ndrivers/hv/hv.c=280=void hv_hyp_synic_enable_regs(unsigned int cpu)\n--\ndrivers/hv/hv.c-295-\t\t\t\t~ms_hyperv.shared_gpa_boundary;\ndrivers/hv/hv.c:296:\t\thv_cpu-\u003ehyp_synic_message_page =\ndrivers/hv/hv.c-297-\t\t\tmemremap(base, HV_HYP_PAGE_SIZE, MEMREMAP_WB | MEMREMAP_DEC);\ndrivers/hv/hv.c:298:\t\tif (!hv_cpu-\u003ehyp_synic_message_page)\ndrivers/hv/hv.c-299-\t\t\tpr_err(\"Fail to map synic message page.\\n\");\ndrivers/hv/hv.c-300-\t} else {\ndrivers/hv/hv.c:301:\t\tsimp.base_simp_gpa = virt_to_phys(hv_cpu-\u003ehyp_synic_message_page)\ndrivers/hv/hv.c-302-\t\t\t\u003e\u003e HV_HYP_PAGE_SHIFT;\n--\ndrivers/hv/hv.c=406=void hv_hyp_synic_disable_regs(unsigned int cpu)\n--\ndrivers/hv/hv.c-431-\tif (ms_hyperv.paravisor_present || hv_root_partition()) {\ndrivers/hv/hv.c:432:\t\tif (hv_cpu-\u003ehyp_synic_message_page) {\ndrivers/hv/hv.c:433:\t\t\tmemunmap(hv_cpu-\u003ehyp_synic_message_page);\ndrivers/hv/hv.c:434:\t\t\thv_cpu-\u003ehyp_synic_message_page = NULL;\ndrivers/hv/hv.c-435-\t\t}\n--\ndrivers/hv/hyperv_vmbus.h=126=struct hv_per_cpu_context {\n--\ndrivers/hv/hyperv_vmbus.h-134-\t */\ndrivers/hv/hyperv_vmbus.h:135:\tvoid *hyp_synic_message_page;\ndrivers/hv/hyperv_vmbus.h-136-\tvoid *hyp_synic_event_page;\n--\ndrivers/hv/mshv_root.h=187=struct hv_synic_pages {\ndrivers/hv/mshv_root.h:188:\tstruct hv_message_page *hyp_synic_message_page;\ndrivers/hv/mshv_root.h-189-\tstruct hv_synic_event_flags_page *synic_event_flags_page;\n--\ndrivers/hv/mshv_synic.c=408=void mshv_isr(void)\n--\ndrivers/hv/mshv_synic.c-410-\tstruct hv_synic_pages *spages = this_cpu_ptr(synic_pages);\ndrivers/hv/mshv_synic.c:411:\tstruct hv_message_page **msg_page = \u0026spages-\u003ehyp_synic_message_page;\ndrivers/hv/mshv_synic.c-412-\tstruct hv_message *msg;\n--\ndrivers/hv/mshv_synic.c=459=static int mshv_synic_cpu_init(unsigned int cpu)\n--\ndrivers/hv/mshv_synic.c-465-\tstruct hv_synic_pages *spages = this_cpu_ptr(synic_pages);\ndrivers/hv/mshv_synic.c:466:\tstruct hv_message_page **msg_page = \u0026spages-\u003ehyp_synic_message_page;\ndrivers/hv/mshv_synic.c-467-\tstruct hv_synic_event_flags_page **event_flags_page =\n--\ndrivers/hv/mshv_synic.c=585=static int mshv_synic_cpu_exit(unsigned int cpu)\n--\ndrivers/hv/mshv_synic.c-589-\tstruct hv_synic_pages *spages = this_cpu_ptr(synic_pages);\ndrivers/hv/mshv_synic.c:590:\tstruct hv_message_page **msg_page = \u0026spages-\u003ehyp_synic_message_page;\ndrivers/hv/mshv_synic.c-591-\tstruct hv_synic_event_flags_page **event_flags_page =\n--\ndrivers/hv/mshv_vtl_main.c=295=static void mshv_vtl_vmbus_isr(void)\n--\ndrivers/hv/mshv_vtl_main.c-305-\tif (smp_processor_id() == 0) {\ndrivers/hv/mshv_vtl_main.c:306:\t\tmsg = (struct hv_message *)per_cpu-\u003ehyp_synic_message_page + VTL2_VMBUS_SINT_INDEX;\ndrivers/hv/mshv_vtl_main.c-307-\t\tmessage_type = READ_ONCE(msg-\u003eheader.message_type);\n--\ndrivers/hv/mshv_vtl_main.c=637=static bool mshv_vtl_process_intercept(void)\n--\ndrivers/hv/mshv_vtl_main.c-644-\tmshv_cpu = this_cpu_ptr(hv_context.cpu_context);\ndrivers/hv/mshv_vtl_main.c:645:\tsynic_message_page = mshv_cpu-\u003ehyp_synic_message_page;\ndrivers/hv/mshv_vtl_main.c-646-\tif (unlikely(!synic_message_page))\n--\ndrivers/hv/mshv_vtl_main.c=874=static void mshv_vtl_read_remote(void *buffer)\n--\ndrivers/hv/mshv_vtl_main.c-876-\tstruct hv_per_cpu_context *mshv_cpu = this_cpu_ptr(hv_context.cpu_context);\ndrivers/hv/mshv_vtl_main.c:877:\tstruct hv_message *msg = (struct hv_message *)mshv_cpu-\u003ehyp_synic_message_page +\ndrivers/hv/mshv_vtl_main.c-878-\t\t\t\t\tVTL2_VMBUS_SINT_INDEX;\n--\ndrivers/hv/vmbus_drv.c=1185=void vmbus_on_msg_dpc(unsigned long data)\n--\ndrivers/hv/vmbus_drv.c-1188-\ndrivers/hv/vmbus_drv.c:1189:\t__vmbus_on_msg_dpc(hv_cpu-\u003ehyp_synic_message_page);\ndrivers/hv/vmbus_drv.c-1190-\t__vmbus_on_msg_dpc(hv_cpu-\u003epara_synic_message_page);\n--\ndrivers/hv/vmbus_drv.c=1327=static void __vmbus_isr(void)\n--\ndrivers/hv/vmbus_drv.c-1334-\ndrivers/hv/vmbus_drv.c:1335:\tvmbus_message_sched(hv_cpu, hv_cpu-\u003ehyp_synic_message_page);\ndrivers/hv/vmbus_drv.c-1336-\tvmbus_message_sched(hv_cpu, hv_cpu-\u003epara_synic_message_page);\n"
}

107/3 2026/09/26 15:34 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 70206
							output: 34
							thoughts: 605
						

Thoughts:
**Uninitialized Memory in Hyper-V SynIC Message and Event Pages**

Okay, so I've been digging into this, and I need to document this properly for my colleagues. Let's see... I was looking at `vmbus_drv.c:1189` and `vmbus_drv.c:1335`, and I've found something interesting regarding the SynIC message and event pages in standard Linux Hyper-V guests. Specifically, when `ms_hyperv.paravisor_present` is false, and `!hv_root_partition()` is true (which is the case for normal VMs), `hyp_synic_message_page` and `hyp_synic_event_page` are allocated using `hv_alloc_page`.

Now, the crucial bit here is what happens *within* `hv_alloc_page`. The allocation uses `hv_alloc_page`, and the allocation code is this:

```c
			ret = hv_alloc_page(&hv_cpu->hyp_synic_message_page,
				decrypt, "hypervisor SynIC msg");
```

The `decrypt` variable is essential: `const bool decrypt = !vmbus_is_confidential();`. Typically, `vmbus_is_confidential()` will be false in the standard configuration, which makes `decrypt` true.

And this is where it gets interesting...

Inside `hv_alloc_page`:

```c
static int hv_alloc_page(void **page, bool decrypt, const char *note)
{
	int ret = 0;

	*page = (void *)__get_free_page(GFP_KERNEL);
	if (!*page)
		return -ENOMEM;

	if (decrypt) {
		ret = set_memory_decrypted((unsigned long)*page, 1);
		if (ret)
			goto failed;
	} else {
		memset(*page, 0, PAGE_SIZE);
	}
	return 0;
```

When `decrypt` is true, the `set_memory_decrypted` is called. The `set_memory_decrypted` function calls `__set_memory_enc_dec(addr, numpages, false)`. However, and this is the crux of the matter: If `cc_platform_has(CC_ATTR_MEM_ENCRYPT)` returns false (which it will, in a standard Hyper-V VM running without confidential computing), `__set_memory_enc_dec` does *nothing* and returns 0. So, `set_memory_decrypted` will return 0. The important thing is because `decrypt` was true, the `else` branch, which would initialize the memory with `memset`, is *skipped*.

Therefore, `__get_free_page(GFP_KERNEL)` returns an *uninitialized* page from the page allocator!

So what's the implication? This means the SynIC message page and event page are allocated and potentially contain whatever was previously stored in those memory locations! This is a serious security vulnerability. I need to know what reads `hyp_synic_message_page`! Let's check `__vmbus_on_msg_dpc` and `vmbus_message_sched`!



108/3 2026/09/26 15:34 tool grepper
0m Args:
{
  "Expression": "__vmbus_on_msg_dpc",
  "PathPrefix": "drivers/hv"
}

Results:
{
  "Output": "drivers/hv/vmbus_drv.c=1031=static void vmbus_onmessage_work(struct work_struct *work)\n--\ndrivers/hv/vmbus_drv.c-1045-\ndrivers/hv/vmbus_drv.c:1046:static void __vmbus_on_msg_dpc(void *message_page_addr)\ndrivers/hv/vmbus_drv.c-1047-{\n--\ndrivers/hv/vmbus_drv.c=1185=void vmbus_on_msg_dpc(unsigned long data)\n--\ndrivers/hv/vmbus_drv.c-1188-\ndrivers/hv/vmbus_drv.c:1189:\t__vmbus_on_msg_dpc(hv_cpu-\u003ehyp_synic_message_page);\ndrivers/hv/vmbus_drv.c:1190:\t__vmbus_on_msg_dpc(hv_cpu-\u003epara_synic_message_page);\ndrivers/hv/vmbus_drv.c-1191-}\n"
}

109/3 2026/09/26 15:34 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 71853
							output: 40
							thoughts: 31
						

Thoughts:
Okay, here's my take on those code locations, viewed through the lens of my expert understanding:

**Diving Deep into `vmbus_drv.c`**

Alright, time to refresh my memory and pinpoint some key areas in `drivers/hv/vmbus_drv.c`. Let's take a look around line 1046, and then again around line 1335. Specifically, I'm thinking about how the driver handles… well, let's see. 1046 is usually around the part where the driver does the initialization work, and 1335 often houses important function calls related to device management. I need to refresh my mind on what the initialization is doing. Is it setting up memory regions? Configuring interrupts?  I need to review those sections with a critical eye, looking for potential areas of concern regarding race conditions or inefficient memory handling, especially given the kernel's complexities. I am looking for something related to device probing. Did I miss anything the last time I looked? Are there any obvious areas for optimization, or perhaps subtle bugs that might only manifest under specific load conditions? And then, around line 1335... this part might be involved in event handling or resource management – perhaps connection negotiation, data transfers, or even device cleanup. I want to carefully examine the interactions between different threads and processes. I'm keen to examine any locks being used, or the lack thereof, in critical sections, and I will be looking for any opportunities to improve throughput or latency. I'm hoping that looking at these two sections together will give me a good handle on some of the core behaviors of the driver. Let's dig in and see what's what!



110/3 2026/09/26 15:34 tool read-file
0m Args:
{
  "File": "drivers/hv/vmbus_drv.c",
  "FirstLine": 1040,
  "LineCount": 50
}

Results:
{
  "Contents": "1040:\t\t\t\t   work);\n1041:\t\tvmbus_onmessage((struct vmbus_channel_message_header *)\n1042:\t\t\t\t\u0026ctx-\u003emsg.payload);\n1043:\t\tkfree(ctx);\n1044:\t}\n1045:\t\n1046:\tstatic void __vmbus_on_msg_dpc(void *message_page_addr)\n1047:\t{\n1048:\t\tstruct hv_message msg_copy, *msg;\n1049:\t\tstruct vmbus_channel_message_header *hdr;\n1050:\t\tenum vmbus_channel_message_type msgtype;\n1051:\t\tconst struct vmbus_channel_message_table_entry *entry;\n1052:\t\tstruct onmessage_work_context *ctx;\n1053:\t\t__u8 payload_size;\n1054:\t\tu32 message_type;\n1055:\t\n1056:\t\tif (!message_page_addr)\n1057:\t\t\treturn;\n1058:\t\tmsg = (struct hv_message *)message_page_addr + VMBUS_MESSAGE_SINT;\n1059:\t\n1060:\t\t/*\n1061:\t\t * 'enum vmbus_channel_message_type' is supposed to always be 'u32' as\n1062:\t\t * it is being used in 'struct vmbus_channel_message_header' definition\n1063:\t\t * which is supposed to match hypervisor ABI.\n1064:\t\t */\n1065:\t\tBUILD_BUG_ON(sizeof(enum vmbus_channel_message_type) != sizeof(u32));\n1066:\t\n1067:\t\t/*\n1068:\t\t * Since the message is in memory shared with the host, an erroneous or\n1069:\t\t * malicious Hyper-V could modify the message while vmbus_on_msg_dpc()\n1070:\t\t * or individual message handlers are executing; to prevent this, copy\n1071:\t\t * the message into private memory.\n1072:\t\t */\n1073:\t\tmemcpy(\u0026msg_copy, msg, sizeof(struct hv_message));\n1074:\t\n1075:\t\tmessage_type = msg_copy.header.message_type;\n1076:\t\tif (message_type == HVMSG_NONE)\n1077:\t\t\t/* no msg */\n1078:\t\t\treturn;\n1079:\t\n1080:\t\thdr = (struct vmbus_channel_message_header *)msg_copy.u.payload;\n1081:\t\tmsgtype = hdr-\u003emsgtype;\n1082:\t\n1083:\t\ttrace_vmbus_on_msg_dpc(hdr);\n1084:\t\n1085:\t\tif (msgtype \u003e= CHANNELMSG_COUNT) {\n1086:\t\t\tWARN_ONCE(1, \"unknown msgtype=%d\\n\", msgtype);\n1087:\t\t\tgoto msg_handled;\n1088:\t\t}\n1089:\t\n"
}

111/3 2026/09/26 15:34 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 73082
							output: 41
							thoughts: 1541
						

Thoughts:
**Uncovering Potential Uninitialized Memory Issues in a Patch Series**

Okay, so I've stumbled upon something interesting here.  This code is checking a hypervisor message, specifically the `message_type` field within a `hv_message` struct. The core of my concern revolves around uninitialized memory, which could lead to security vulnerabilities. Initially, it retrieves this message from a page allocated by the hypervisor and copies it:

```c
msg = (struct hv_message *)message_page_addr + VMBUS_MESSAGE_SINT;
memcpy(&msg_copy, msg, sizeof(struct hv_message));
message_type = msg_copy.header.message_type;
if (message_type == HVMSG_NONE) return;
```

It seems that if the page *was* uninitialized when allocated, the `msg->header.message_type` could contain garbage, which is then copied to `msg_copy`. This could bypass the check.

The page is mapped to the hypervisor via the MSR SIMP, as seen in the call to `hv_hyp_synic_enable_regs`.

```c
simp.base_simp_gpa = virt_to_phys(hv_cpu->hyp_synic_message_page) >> HV_HYP_PAGE_SHIFT;
```

My initial question was, "Is syzkaller running on Hyper-V?"  Syzkaller typically targets QEMU/KVM. But wait, can syzkaller fuzz Hyper-V drivers? And if so, does syzkaller have KMSAN enabled in x86_64, as that would expose the uninitialized memory issue. It turns out, that it does! So, this is a real concern!

So, the first thing I want to do is to go through the changes, looking at *all* the other files to see if the issue is a one-off, or if there are other memory initialization concerns in the patch series. I need to be *thorough*. So, I'll go through this file by file.

Here's my analysis:

*   **File 1: `arch/arm/mm/dma-mapping.c`** - No relevant changes
*   **File 2: `arch/arm64/mm/pageattr.c`** - No relevant changes
*   **File 3: `arch/powerpc/platforms/pseries/svm.c`** - No relevant changes
*   **File 4: `arch/s390/mm/init.c`** - No relevant changes
*   **File 5: `arch/x86/coco/tdx/tdx.c`** - No relevant changes
*   **File 6: `arch/x86/hyperv/hv_init.c`** - I've found an interesting area in the `hv_cpu_init` function.

```c
			if (*hvp && !ms_hyperv.paravisor_present && hv_isolation_type_snp()) {
				int ret;
				ret = set_memory_decrypted((unsigned long)*hvp, 1);
				WARN_ON_ONCE(ret);
			}
```

Here, `hv_isolation_type_snp()` is checked.  I know that SNP guests use `amd_enc_status_change_prepare`, which *zeroes* the page in the diff. So removing `memset(*hvp, 0, PAGE_SIZE)` here is actually fine.

*   **File 7: `arch/x86/hyperv/ivm.c`** - I found this function, which has the following code

```c
static int hv_vtom_clear_present(unsigned long kbuffer, int pagecount, bool enc)
{
	if (!enc)
		memset((void *)kbuffer, 0,
		       (size_t)pagecount << PAGE_SHIFT);

	return set_memory_np(kbuffer, pagecount);
}
```

This is *important* and safe because it zeros the memory if encryption is *not* enabled (`!enc`).

*   **File 8: `arch/x86/kernel/kvmclock.c`** - Now, looking at `kvmclock.c`:

```c
	if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) {
		r = set_memory_decrypted((unsigned long)hvclock_mem,
					 1UL << order);
		if (r) {
			__free_pages(p, order);
			return -ENOMEM


112/3 2026/09/26 15:35 tool grepper
0m Args:
{
  "Expression": "system_heap_mmap",
  "PathPrefix": "drivers/dma-buf/heaps/system_heap.c"
}

Results:
{
  "Output": "drivers/dma-buf/heaps/system_heap.c=178=static int system_heap_dma_buf_end_cpu_access(struct dma_buf *dmabuf,\n--\ndrivers/dma-buf/heaps/system_heap.c-198-\ndrivers/dma-buf/heaps/system_heap.c:199:static int system_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)\ndrivers/dma-buf/heaps/system_heap.c-200-{\n--\ndrivers/dma-buf/heaps/system_heap.c=339=static const struct dma_buf_ops system_heap_buf_ops = {\n--\ndrivers/dma-buf/heaps/system_heap.c-345-\t.end_cpu_access = system_heap_dma_buf_end_cpu_access,\ndrivers/dma-buf/heaps/system_heap.c:346:\t.mmap = system_heap_mmap,\ndrivers/dma-buf/heaps/system_heap.c-347-\t.vmap = system_heap_vmap,\n"
}

113/3 2026/09/26 15:35 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 75996
							output: 43
							thoughts: 28
						

Thoughts:
Okay, here's my interpretation of that thought process, assuming I'm already quite familiar with DMA-BUF and Linux kernel internals:

**Analyzing `system_heap.c` around line 199**

Alright, the request is very specific. I need to dive into the `drivers/dma-buf/heaps/system_heap.c` file. The fact that the line number is mentioned, line 199, suggests there's a particular section of code that needs scrutiny. It is unlikely a random line.

Given the file name, it's immediately clear that this concerns the system heap implementation within the DMA-BUF framework. My brain's already starting to conjure up the basic structure: allocation functions, free routines, how it interacts with the underlying memory management, and perhaps any specialized handling for coherent memory or device-specific constraints.

I need to quickly grasp the code around line 199. It's probably related to allocation or deallocation and could relate to one of the following:

*   **Initialization**: I need to see if it is involved with setting up a struct, or variables.
*   **Memory Management**: This could involve the actual physical memory mapping or unmapping operations.
*   **Synchronization**: DMA operations can involve the need for synchronization primitives like spinlocks or mutexes. Line 199 might be a point where those are being acquired or released to guarantee thread safety.
*   **Error Handling**: I want to see if the code around this line handles potential allocation failures or other error conditions that can arise when dealing with the system heap.
*   **Debugging**: This might be a print line that helps to aid debugging, and to understand the heap state.

I'm mentally preparing for potential areas of focus and what is most likely to be around line 199 in the `system_heap.c` file.



114/3 2026/09/26 15:35 tool read-file
0m Args:
{
  "File": "drivers/dma-buf/heaps/system_heap.c",
  "FirstLine": 198,
  "LineCount": 40
}

Results:
{
  "Contents": " 198:\t\n 199:\tstatic int system_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)\n 200:\t{\n 201:\t\tstruct system_heap_buffer *buffer = dmabuf-\u003epriv;\n 202:\t\tstruct sg_table *table = \u0026buffer-\u003esg_table;\n 203:\t\tunsigned long addr = vma-\u003evm_start;\n 204:\t\tunsigned long pgoff = vma-\u003evm_pgoff;\n 205:\t\tstruct scatterlist *sg;\n 206:\t\tpgprot_t prot;\n 207:\t\tint i, ret;\n 208:\t\n 209:\t\tprot = vma-\u003evm_page_prot;\n 210:\t\tif (cc_shared_buffer(buffer))\n 211:\t\t\tprot = pgprot_decrypted(prot);\n 212:\t\n 213:\t\tfor_each_sgtable_sg(table, sg, i) {\n 214:\t\t\tunsigned long n = sg-\u003elength \u003e\u003e PAGE_SHIFT;\n 215:\t\n 216:\t\t\tif (pgoff \u003c n)\n 217:\t\t\t\tbreak;\n 218:\t\t\tpgoff -= n;\n 219:\t\t}\n 220:\t\n 221:\t\tfor (; sg \u0026\u0026 addr \u003c vma-\u003evm_end; sg = sg_next(sg)) {\n 222:\t\t\tunsigned long n = (sg-\u003elength \u003e\u003e PAGE_SHIFT) - pgoff;\n 223:\t\t\tstruct page *page = sg_page(sg) + pgoff;\n 224:\t\t\tunsigned long size = n \u003c\u003c PAGE_SHIFT;\n 225:\t\n 226:\t\t\tif (addr + size \u003e vma-\u003evm_end)\n 227:\t\t\t\tsize = vma-\u003evm_end - addr;\n 228:\t\n 229:\t\t\tret = remap_pfn_range(vma, addr, page_to_pfn(page), size, prot);\n 230:\t\t\tif (ret)\n 231:\t\t\t\treturn ret;\n 232:\t\n 233:\t\t\taddr += size;\n 234:\t\t\tpgoff = 0;\n 235:\t\t}\n 236:\t\n 237:\t\treturn 0;\n"
}

115/3 2026/09/26 15:35 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 77076
							output: 38
							thoughts: 462
						

Thoughts:
**Analyzing `system_heap_alloc_order` and `alloc_cc_shared_pages`**

Okay, hold on a second. I need to dig into this `system_heap_alloc_order` function. Right, here it is:

```c
static struct page *system_heap_alloc_order(unsigned int order,
	     gfp_t flags, bool cc_shared)
{
	struct cc_shared_pages mem;

	if (!cc_shared)
		return alloc_pages(flags, order);

	/* The shared granule can raise the actual allocation order. */
	flags |= __GFP_COMP;
	if (alloc_cc_shared_pages(flags, PAGE_SIZE << order, &mem))
		return NULL;

	return mem.page;
}
```

Now, the key here is that `cc_shared` boolean. If it's false, we're just doing a standard `alloc_pages`. But if it's true, we call `alloc_cc_shared_pages`, which is what I need to understand. Let's look at `alloc_cc_shared_pages`:

```c
int alloc_cc_shared_pages_node(int nid, gfp_t gfp,
			       size_t requested,
			       struct cc_shared_pages *mem)
{
	struct page *page;
	unsigned int order;

	if (!mem || !requested)
		return -EINVAL;

	if (cc_platform_has(CC_ATTR_MEM_ENCRYPT))
		return __alloc_cc_shared_pages_node(nid, gfp, requested, mem);

	order = get_order(requested);
	if (order > MAX_PAGE_ORDER)
		return -EINVAL;

	if (nid == NUMA_NO_NODE)
		page = alloc_pages(gfp, order);
	else
		page = alloc_pages_node(nid, gfp, order);
	if (!page)
		return -ENOMEM;

	mem->page = page;
	mem->shared_size = requested;
	return 0;
}
```

Okay, so `alloc_cc_shared_pages` *also* seems to be calling `alloc_pages` or `alloc_pages_node`, after getting the order. The interesting conditional here is related to `CC_ATTR_MEM_ENCRYPT`. We need to figure out what triggers this function, and how `cc_shared` gets set in the first place, and what `CC_ATTR_MEM_ENCRYPT` does. The comment mentions shared memory. Where is this used in `system_heap`? I need to understand the connection between these shared pages and the overall system heap.

Alright, let's switch gears and go hunt for the `cc_shared` field within the `system_heap` itself. I need to see when it's set. Looking in `drivers/dma-buf/heaps/system_heap.c`, and where heaps are registered is the best place to start.



116/3 2026/09/26 15:35 tool grepper
0m Args:
{
  "Expression": "cc_shared",
  "PathPrefix": "drivers/dma-buf/heaps/system_heap.c"
}

Results:
{
  "Output": "drivers/dma-buf/heaps/system_heap.c-13-#include \u003clinux/cc_platform.h\u003e\ndrivers/dma-buf/heaps/system_heap.c:14:#include \u003clinux/cc_shared.h\u003e\ndrivers/dma-buf/heaps/system_heap.c-15-#include \u003clinux/dma-buf.h\u003e\n--\ndrivers/dma-buf/heaps/system_heap.c=27=struct system_heap_priv {\ndrivers/dma-buf/heaps/system_heap.c:28:\tbool cc_shared;\ndrivers/dma-buf/heaps/system_heap.c-29-};\n--\ndrivers/dma-buf/heaps/system_heap.c=31=struct system_heap_buffer {\n--\ndrivers/dma-buf/heaps/system_heap.c-38-\tvoid *vaddr;\ndrivers/dma-buf/heaps/system_heap.c:39:\tbool cc_shared;\ndrivers/dma-buf/heaps/system_heap.c-40-};\n--\ndrivers/dma-buf/heaps/system_heap.c=42=struct dma_heap_attachment {\n--\ndrivers/dma-buf/heaps/system_heap.c-46-\tbool mapped;\ndrivers/dma-buf/heaps/system_heap.c:47:\tbool cc_shared;\ndrivers/dma-buf/heaps/system_heap.c-48-};\ndrivers/dma-buf/heaps/system_heap.c-49-\ndrivers/dma-buf/heaps/system_heap.c:50:#define cc_shared_buffer(b) (IS_ENABLED(CONFIG_DMABUF_HEAPS_SYSTEM_CC_SHARED) \u0026\u0026 \\\ndrivers/dma-buf/heaps/system_heap.c:51:\t\t\t\t(b)-\u003ecc_shared)\ndrivers/dma-buf/heaps/system_heap.c-52-\n--\ndrivers/dma-buf/heaps/system_heap.c=85=static int system_heap_attach(struct dma_buf *dmabuf,\n--\ndrivers/dma-buf/heaps/system_heap.c-104-\ta-\u003emapped = false;\ndrivers/dma-buf/heaps/system_heap.c:105:\ta-\u003ecc_shared = buffer-\u003ecc_shared;\ndrivers/dma-buf/heaps/system_heap.c-106-\n--\ndrivers/dma-buf/heaps/system_heap.c=130=static struct sg_table *system_heap_map_dma_buf(struct dma_buf_attachment *attachment,\n--\ndrivers/dma-buf/heaps/system_heap.c-137-\ndrivers/dma-buf/heaps/system_heap.c:138:\tattrs = cc_shared_buffer(a) ? DMA_ATTR_CC_SHARED : 0;\ndrivers/dma-buf/heaps/system_heap.c-139-\tret = dma_map_sgtable(attachment-\u003edev, table, direction, attrs);\n--\ndrivers/dma-buf/heaps/system_heap.c=199=static int system_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)\n--\ndrivers/dma-buf/heaps/system_heap.c-209-\tprot = vma-\u003evm_page_prot;\ndrivers/dma-buf/heaps/system_heap.c:210:\tif (cc_shared_buffer(buffer))\ndrivers/dma-buf/heaps/system_heap.c-211-\t\tprot = pgprot_decrypted(prot);\n--\ndrivers/dma-buf/heaps/system_heap.c=240=static void *system_heap_do_vmap(struct system_heap_buffer *buffer)\n--\ndrivers/dma-buf/heaps/system_heap.c-258-\tprot = PAGE_KERNEL;\ndrivers/dma-buf/heaps/system_heap.c:259:\tif (cc_shared_buffer(buffer))\ndrivers/dma-buf/heaps/system_heap.c-260-\t\tprot = pgprot_decrypted(prot);\n--\ndrivers/dma-buf/heaps/system_heap.c=298=static void system_heap_vunmap(struct dma_buf *dmabuf, struct iosys_map *map)\n--\ndrivers/dma-buf/heaps/system_heap.c-310-\ndrivers/dma-buf/heaps/system_heap.c:311:static void system_heap_free_page(struct page *page, bool cc_shared)\ndrivers/dma-buf/heaps/system_heap.c-312-{\ndrivers/dma-buf/heaps/system_heap.c:313:\tstruct cc_shared_pages mem;\ndrivers/dma-buf/heaps/system_heap.c-314-\ndrivers/dma-buf/heaps/system_heap.c:315:\tif (!cc_shared) {\ndrivers/dma-buf/heaps/system_heap.c-316-\t\t__free_pages(page, compound_order(page));\n--\ndrivers/dma-buf/heaps/system_heap.c-321-\tmem.shared_size = page_size(page);\ndrivers/dma-buf/heaps/system_heap.c:322:\tfree_cc_shared_pages(\u0026mem);\ndrivers/dma-buf/heaps/system_heap.c-323-}\n--\ndrivers/dma-buf/heaps/system_heap.c=325=static void system_heap_dma_buf_release(struct dma_buf *dmabuf)\n--\ndrivers/dma-buf/heaps/system_heap.c-333-\tfor_each_sgtable_sg(table, sg, i)\ndrivers/dma-buf/heaps/system_heap.c:334:\t\tsystem_heap_free_page(sg_page(sg), cc_shared_buffer(buffer));\ndrivers/dma-buf/heaps/system_heap.c-335-\tsg_free_table(table);\n--\ndrivers/dma-buf/heaps/system_heap.c=352=static struct page *system_heap_alloc_order(unsigned int order,\ndrivers/dma-buf/heaps/system_heap.c:353:\t     gfp_t flags, bool cc_shared)\ndrivers/dma-buf/heaps/system_heap.c-354-{\ndrivers/dma-buf/heaps/system_heap.c:355:\tstruct cc_shared_pages mem;\ndrivers/dma-buf/heaps/system_heap.c-356-\ndrivers/dma-buf/heaps/system_heap.c:357:\tif (!cc_shared)\ndrivers/dma-buf/heaps/system_heap.c-358-\t\treturn alloc_pages(flags, order);\n--\ndrivers/dma-buf/heaps/system_heap.c-361-\tflags |= __GFP_COMP;\ndrivers/dma-buf/heaps/system_heap.c:362:\tif (alloc_cc_shared_pages(flags, PAGE_SIZE \u003c\u003c order, \u0026mem))\ndrivers/dma-buf/heaps/system_heap.c-363-\t\treturn NULL;\n--\ndrivers/dma-buf/heaps/system_heap.c=368=static struct page *alloc_largest_available(unsigned long size,\ndrivers/dma-buf/heaps/system_heap.c:369:\t\tunsigned int max_order, bool cc_shared)\ndrivers/dma-buf/heaps/system_heap.c-370-{\n--\ndrivers/dma-buf/heaps/system_heap.c-383-\t\t\tflags |= __GFP_ACCOUNT;\ndrivers/dma-buf/heaps/system_heap.c:384:\t\tpage = system_heap_alloc_order(orders[i], flags, cc_shared);\ndrivers/dma-buf/heaps/system_heap.c-385-\t\tif (!page)\n--\ndrivers/dma-buf/heaps/system_heap.c=392=static struct dma_buf *system_heap_allocate(struct dma_heap *heap,\n--\ndrivers/dma-buf/heaps/system_heap.c-398-\tDEFINE_DMA_BUF_EXPORT_INFO(exp_info);\ndrivers/dma-buf/heaps/system_heap.c:399:\tstruct cc_shared_layout layout;\ndrivers/dma-buf/heaps/system_heap.c-400-\tunsigned long size_remaining = len;\n--\ndrivers/dma-buf/heaps/system_heap.c-403-\tstruct system_heap_priv *priv = dma_heap_get_drvdata(heap);\ndrivers/dma-buf/heaps/system_heap.c:404:\tbool cc_shared = priv-\u003ecc_shared;\ndrivers/dma-buf/heaps/system_heap.c-405-\tstruct dma_buf *dmabuf;\n--\ndrivers/dma-buf/heaps/system_heap.c-411-\ndrivers/dma-buf/heaps/system_heap.c:412:\tif (cc_shared) {\ndrivers/dma-buf/heaps/system_heap.c-413-\t\tint err;\ndrivers/dma-buf/heaps/system_heap.c-414-\ndrivers/dma-buf/heaps/system_heap.c:415:\t\terr = cc_shared_calc_layout(len, \u0026layout);\ndrivers/dma-buf/heaps/system_heap.c-416-\t\tif (err)\n--\ndrivers/dma-buf/heaps/system_heap.c-429-\tbuffer-\u003elen = len;\ndrivers/dma-buf/heaps/system_heap.c:430:\tbuffer-\u003ecc_shared = cc_shared;\ndrivers/dma-buf/heaps/system_heap.c-431-\n--\ndrivers/dma-buf/heaps/system_heap.c-444-\t\tpage = alloc_largest_available(size_remaining, max_order,\ndrivers/dma-buf/heaps/system_heap.c:445:\t\t\t\t\t       cc_shared);\ndrivers/dma-buf/heaps/system_heap.c-446-\t\tif (!page)\n--\ndrivers/dma-buf/heaps/system_heap.c-484-\tfor_each_sgtable_sg(table, sg, i)\ndrivers/dma-buf/heaps/system_heap.c:485:\t\tsystem_heap_free_page(sg_page(sg), cc_shared);\ndrivers/dma-buf/heaps/system_heap.c-486-\tsg_free_table(table);\n--\ndrivers/dma-buf/heaps/system_heap.c-488-\tlist_for_each_entry_safe(page, tmp_page, \u0026pages, lru)\ndrivers/dma-buf/heaps/system_heap.c:489:\t\tsystem_heap_free_page(page, cc_shared);\ndrivers/dma-buf/heaps/system_heap.c-490-\tkfree(buffer);\n--\ndrivers/dma-buf/heaps/system_heap.c=499=static struct system_heap_priv system_heap_priv = {\ndrivers/dma-buf/heaps/system_heap.c:500:\t.cc_shared = false,\ndrivers/dma-buf/heaps/system_heap.c-501-};\ndrivers/dma-buf/heaps/system_heap.c-502-\ndrivers/dma-buf/heaps/system_heap.c:503:static struct system_heap_priv system_heap_cc_shared_priv = {\ndrivers/dma-buf/heaps/system_heap.c:504:\t.cc_shared = true,\ndrivers/dma-buf/heaps/system_heap.c-505-};\n--\ndrivers/dma-buf/heaps/system_heap.c=507=static int __init system_heap_create(void)\n--\ndrivers/dma-buf/heaps/system_heap.c-524-\ndrivers/dma-buf/heaps/system_heap.c:525:\texp_info.name = \"system_cc_shared\";\ndrivers/dma-buf/heaps/system_heap.c:526:\texp_info.priv = \u0026system_heap_cc_shared_priv;\ndrivers/dma-buf/heaps/system_heap.c-527-\tsys_heap = dma_heap_add(\u0026exp_info);\n"
}

117/3 2026/09/26 15:35 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 81505
							output: 43
							thoughts: 80
						

Thoughts:
Here's my take on the situation:

**Investigating the System CC Shared Heap**

Okay, so I'm dealing with the `system_cc_shared` heap. The key point is that its presence hinges on the `CONFIG_DMABUF_HEAPS_SYSTEM_CC_SHARED` configuration option. Now, before I proceed any further with this investigation, I need to make a quick check. I'll need to confirm that that particular configuration flag is actually enabled. Once I've verified that, I should take a look at the relevant source code, specifically lines 500-535 within the `drivers/dma-buf/heaps/system_heap.c` file. Those lines should contain the core logic and any conditional compilation that dictates the behavior of the heap and it's dependence on the CONFIG.



118/3 2026/09/26 15:35 tool read-file
0m Args:
{
  "File": "drivers/dma-buf/heaps/system_heap.c",
  "FirstLine": 500,
  "LineCount": 40
}

Results:
{
  "Contents": " 500:\t\t.cc_shared = false,\n 501:\t};\n 502:\t\n 503:\tstatic struct system_heap_priv system_heap_cc_shared_priv = {\n 504:\t\t.cc_shared = true,\n 505:\t};\n 506:\t\n 507:\tstatic int __init system_heap_create(void)\n 508:\t{\n 509:\t\tstruct dma_heap_export_info exp_info;\n 510:\t\tstruct dma_heap *sys_heap;\n 511:\t\n 512:\t\texp_info.name = \"system\";\n 513:\t\texp_info.ops = \u0026system_heap_ops;\n 514:\t\texp_info.priv = \u0026system_heap_priv;\n 515:\t\n 516:\t\tsys_heap = dma_heap_add(\u0026exp_info);\n 517:\t\tif (IS_ERR(sys_heap))\n 518:\t\t\treturn PTR_ERR(sys_heap);\n 519:\t\n 520:\t\tif (IS_ENABLED(CONFIG_HIGHMEM) ||\n 521:\t\t    !IS_ENABLED(CONFIG_DMABUF_HEAPS_SYSTEM_CC_SHARED) ||\n 522:\t\t    !cc_platform_has(CC_ATTR_MEM_ENCRYPT))\n 523:\t\t\treturn 0;\n 524:\t\n 525:\t\texp_info.name = \"system_cc_shared\";\n 526:\t\texp_info.priv = \u0026system_heap_cc_shared_priv;\n 527:\t\tsys_heap = dma_heap_add(\u0026exp_info);\n 528:\t\tif (IS_ERR(sys_heap))\n 529:\t\t\treturn PTR_ERR(sys_heap);\n 530:\t\n 531:\t\treturn 0;\n 532:\t}\n 533:\tmodule_init(system_heap_create);\n 534:\t\n 535:\tMODULE_DESCRIPTION(\"DMA-BUF System Heap\");\n 536:\tMODULE_LICENSE(\"GPL\");\n 537:\tMODULE_IMPORT_NS(\"DMA_BUF\");\n 538:\tMODULE_IMPORT_NS(\"DMA_BUF_HEAP\");\n"
}

119/3 2026/09/26 15:35 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 82404
							output: 37
							thoughts: 1930
						

Thoughts:
**Memory Zeroing in the Kernel: A Deep Dive**

Okay, so I'm digging into how memory is zeroed in the kernel, particularly within the context of DMA heaps and shared memory. The initial focus is on the `system_cc_shared` heap. I've realized this heap is *only* created if the platform supports memory encryption (`cc_platform_has(CC_ATTR_MEM_ENCRYPT)`). If that feature isn't present, we're dealing with the regular "system" heap. The crucial point is that the standard "system" heap *always* gets zeroed because of `__GFP_ZERO` being set in the flags. And if `cc_shared` is true, which indicates encryption is enabled, then shared memory zeroing goes through a path that also results in zeroing via `memset`. So, both paths for "system" memory end up with zeroed memory, which is good.

Then I shifted to how the length in `sglist` is handled.  I realized that when `cc_shared` is enabled, the allocated memory can be rounded up to a shared granule size (like 2MB). However, the `sg_remaining` variable and the `sg` table *only* expose the user-requested length.  I checked how the memory is mapped via `system_heap_mmap`, which ensures that the `sg->length` (the user-requested length) prevents the mapping from exceeding the requested size. This is important to ensure that the user doesn't end up accidentally reading or writing beyond the intended memory region.

After that, I wanted to double-check that `alloc_cc_shared_pages` *also* returns zeroed memory. Reviewing `mm/cc_shared.c` confirms that the function either calls `alloc_pages` (with `__GFP_ZERO` if `cc_platform_has` is false, hence zeroed), or uses the `__alloc_cc_shared_pages_node` which eventually leads to zeroing too. The callers of `alloc_cc_shared_pages` show the `__GFP_ZERO` flag is passed via different means.

I then started investigating the other callers of `alloc_cc_shared_pages` and its associated functions in `drivers/irqchip/irq-gic-v3-its.c`.  I noticed some interesting logic there, where `its_alloc_pages` calls `alloc_cc_shared_pages_node` with either `GFP_KERNEL | __GFP_ZERO` or `GFP_ATOMIC | __GFP_ZERO`.  This confirms that the memory allocated by this function is also zeroed, which is a key consistency point. Also, checking for its use in `vpe_l1_prealloc` ensures the zeroing is applied in the right place, regardless of pre-allocation.

Finally, I revisited some other drivers I had previously looked at, like `drivers/hv/`, but the most significant takeaway from a diff check in `drivers/hv/hv_common.c` is that a `memset` call, that was present earlier to zero a memory region, was removed.  Given the zeroing already happens via the `alloc_pages` paths, the explicit `memset` call was redundant. This means the memory will be zeroed by the calls to the allocation functions that already use `__GFP_ZERO`.



120/3 2026/09/26 15:35 tool read-file
0m Args:
{
  "File": "drivers/hv/hv_common.c",
  "FirstLine": 470,
  "LineCount": 50
}

Results:
{
  "Contents": " 470:\t */\n 471:\t\n 472:\tint hv_common_cpu_init(unsigned int cpu)\n 473:\t{\n 474:\t\tvoid **inputarg, **outputarg;\n 475:\t\tu8 **synic_eventring_tail;\n 476:\t\tu64 msr_vp_index;\n 477:\t\tgfp_t flags;\n 478:\t\tconst int pgcount = hv_output_page_exists() ? 2 : 1;\n 479:\t\tvoid *mem;\n 480:\t\tint ret = 0;\n 481:\t\n 482:\t\t/* hv_cpu_init() can be called with IRQs disabled from hv_resume() */\n 483:\t\tflags = irqs_disabled() ? GFP_ATOMIC : GFP_KERNEL;\n 484:\t\n 485:\t\tinputarg = (void **)this_cpu_ptr(hyperv_pcpu_input_arg);\n 486:\t\n 487:\t\t/*\n 488:\t\t * The per-cpu memory is already allocated if this CPU was previously\n 489:\t\t * online and then taken offline\n 490:\t\t */\n 491:\t\tif (!*inputarg) {\n 492:\t\t\tmem = kmalloc_array(pgcount, HV_HYP_PAGE_SIZE, flags);\n 493:\t\t\tif (!mem)\n 494:\t\t\t\treturn -ENOMEM;\n 495:\t\n 496:\t\t\tif (hv_output_page_exists()) {\n 497:\t\t\t\toutputarg = (void **)this_cpu_ptr(hyperv_pcpu_output_arg);\n 498:\t\t\t\t*outputarg = (char *)mem + HV_HYP_PAGE_SIZE;\n 499:\t\t\t}\n 500:\t\n 501:\t\t\tif (!ms_hyperv.paravisor_present \u0026\u0026\n 502:\t\t\t    (hv_isolation_type_snp() || hv_isolation_type_tdx())) {\n 503:\t\t\t\tret = set_memory_decrypted((unsigned long)mem, pgcount);\n 504:\t\t\t\tif (ret) {\n 505:\t\t\t\t\t/* It may be unsafe to free 'mem' */\n 506:\t\t\t\t\treturn ret;\n 507:\t\t\t\t}\n 508:\t\t\t}\n 509:\t\n 510:\t\t\t/*\n 511:\t\t\t * In a fully enlightened TDX/SNP VM with more than 64 VPs, if\n 512:\t\t\t * hyperv_pcpu_input_arg is not NULL, set_memory_decrypted() -\u003e\n 513:\t\t\t * ... -\u003e cpa_flush()-\u003e ... -\u003e __send_ipi_mask_ex() tries to\n 514:\t\t\t * use hyperv_pcpu_input_arg as the hypercall input page, which\n 515:\t\t\t * must be a decrypted page in such a VM, but the page is still\n 516:\t\t\t * encrypted before set_memory_decrypted() returns. Fix this by\n 517:\t\t\t * setting *inputarg after the above set_memory_decrypted(): if\n 518:\t\t\t * hyperv_pcpu_input_arg is NULL, __send_ipi_mask_ex() returns\n 519:\t\t\t * HV_STATUS_INVALID_PARAMETER immediately, and the function\n"
}

121/3 2026/09/26 15:35 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 85943
							output: 23
							thoughts: 1380
						

Thoughts:
**Uninitialized Memory in Hyper-V Hypercall Input/Output Buffers!**

Okay, I need to get this straight. I'm looking at `drivers/hv/hv_common.c`, specifically lines 490-508, and I'm seeing a critical issue. First, `mem` is allocated using `kmalloc_array`, which is *not* `kzalloc`. Right there, alarm bells. The code allocates memory, `mem`, using `kmalloc_array`, which means the memory is uninitialized. Normally, I would expect a `kcalloc` or a `memset` to zero it, especially for something potentially holding sensitive hypercall input/output data.

Then, there's a conditional block that involves `set_memory_decrypted`, which I assumed would zero it. But wait, that's only called when `!ms_hyperv.paravisor_present && (hv_isolation_type_snp() || hv_isolation_type_tdx())`. *That's* only for specific, more isolated, hardware scenarios like SNP or TDX. For a standard Hyper-V VM, that condition is FALSE! In that case, the `set_memory_decrypted` is never called. And, cruelly, the code *deleted* the `memset` operation that was there previously, thinking the `set_memory_decrypted` would do the zeroing. But that's not what happened!

I just checked again, looking at line 483, `flags = irqs_disabled() ? GFP_ATOMIC : GFP_KERNEL;`. There's no `__GFP_ZERO` being used with that. So, `kmalloc_array` *definitely* doesn't zero it.

So, for normal Hyper-V VMs that don't have that hardware isolation, the following is true:
1.  `mem` is allocated using `kmalloc_array` (which is uninitialized heap memory).
2.  `set_memory_decrypted` is not called!
3.  The `memset` was removed.

This means the `mem` memory, which I see used as `hyperv_pcpu_input_arg` and `hyperv_pcpu_output_arg`, is left *completely uninitialized*! This is a massive problem. This memory is the input and output buffer used across the kernel for Hyper-V hypercalls. I *have* to check which hypercalls are using this `hyperv_pcpu_input_arg`! This could expose sensitive information or lead to serious security vulnerabilities due to uninitialized memory being used! This patch, if it went out like this, could cause serious issues!



122/3 2026/09/26 15:35 tool grepper
0m Args:
{
  "Expression": "hyperv_pcpu_input_arg"
}

Results:
{
  "Output": "arch/x86/hyperv/hv_apic.c=110=static bool __send_ipi_mask_ex(const struct cpumask *mask, int vector,\n--\narch/x86/hyperv/hv_apic.c-121-\tlocal_irq_save(flags);\narch/x86/hyperv/hv_apic.c:122:\tipi_arg = *this_cpu_ptr(hyperv_pcpu_input_arg);\narch/x86/hyperv/hv_apic.c-123-\n--\narch/x86/hyperv/hv_crash.c=243=static void hv_notify_prepare_hyp(void)\n--\narch/x86/hyperv/hv_crash.c-248-\narch/x86/hyperv/hv_crash.c:249:\tinput = *this_cpu_ptr(hyperv_pcpu_input_arg);\narch/x86/hyperv/hv_crash.c-250-\tcda = \u0026input-\u003einput.crashdump_input;\n--\narch/x86/hyperv/hv_crash.c=283=static noinline __noclone void crash_nmi_callback(struct pt_regs *regs)\n--\narch/x86/hyperv/hv_crash.c-313-\narch/x86/hyperv/hv_crash.c:314:\tinput = *this_cpu_ptr(hyperv_pcpu_input_arg);\narch/x86/hyperv/hv_crash.c-315-\tmemset(input, 0, sizeof(*input));\n--\narch/x86/hyperv/hv_crash.c=585=void hv_root_crash_init(void)\n--\narch/x86/hyperv/hv_crash.c-606-\tlocal_irq_save(flags);\narch/x86/hyperv/hv_crash.c:607:\tinput = *this_cpu_ptr(hyperv_pcpu_input_arg);\narch/x86/hyperv/hv_crash.c-608-\toutput = *this_cpu_ptr(hyperv_pcpu_output_arg);\n--\narch/x86/hyperv/hv_init.c=454=void __init hyperv_init(void)\n--\narch/x86/hyperv/hv_init.c-504-\t * page to Hyper-V, e.g. hv_post_message() uses the per-CPU page\narch/x86/hyperv/hv_init.c:505:\t * hyperv_pcpu_input_arg, which is decrypted if no paravisor is present.\narch/x86/hyperv/hv_init.c-506-\t *\n--\narch/x86/hyperv/hv_init.c-508-\t * which are handled by the paravisor and the VM must use an encrypted\narch/x86/hyperv/hv_init.c:509:\t * input page: in such a VM, the hyperv_pcpu_input_arg is encrypted and\narch/x86/hyperv/hv_init.c-510-\t * used in the hypercalls, e.g. see hv_mark_gpa_visibility() and\n--\narch/x86/hyperv/hv_init.c-513-\t * 2. HVCALL_POST_MESSAGE: the input page must be a decrypted page, i.e.\narch/x86/hyperv/hv_init.c:514:\t * hv_post_message() in such a VM can't use the encrypted hyperv_pcpu_input_arg;\narch/x86/hyperv/hv_init.c-515-\t * instead, hv_post_message() uses the post_msg_page, which is decrypted\n--\narch/x86/hyperv/hv_init.c=717=int hv_apicid_to_vp_index(u32 apic_id)\n--\narch/x86/hyperv/hv_init.c-726-\narch/x86/hyperv/hv_init.c:727:\tinput = *this_cpu_ptr(hyperv_pcpu_input_arg);\narch/x86/hyperv/hv_init.c-728-\tmemset(input, 0, sizeof(*input));\n--\narch/x86/hyperv/hv_vtl.c=134=static int hv_vtl_bringup_vcpu(u32 target_vp_index, int cpu, u64 eip_ignored)\n--\narch/x86/hyperv/hv_vtl.c-163-\narch/x86/hyperv/hv_vtl.c:164:\tinput = *this_cpu_ptr(hyperv_pcpu_input_arg);\narch/x86/hyperv/hv_vtl.c-165-\tmemset(input, 0, sizeof(*input));\n--\narch/x86/hyperv/irqdomain.c=16=static int hv_map_interrupt(union hv_device_id hv_devid, bool level,\n--\narch/x86/hyperv/irqdomain.c-27-\narch/x86/hyperv/irqdomain.c:28:\tinput = *this_cpu_ptr(hyperv_pcpu_input_arg);\narch/x86/hyperv/irqdomain.c-29-\toutput = *this_cpu_ptr(hyperv_pcpu_output_arg);\n--\narch/x86/hyperv/irqdomain.c=73=static int hv_unmap_interrupt(u64 id, struct hv_interrupt_entry *irq_entry)\n--\narch/x86/hyperv/irqdomain.c-79-\tlocal_irq_save(flags);\narch/x86/hyperv/irqdomain.c:80:\tinput = *this_cpu_ptr(hyperv_pcpu_input_arg);\narch/x86/hyperv/irqdomain.c-81-\n--\narch/x86/hyperv/ivm.c=632=static void hv_vtom_kexec_finish(void)\n--\narch/x86/hyperv/ivm.c-640-\tlocal_irq_save(flags);\narch/x86/hyperv/ivm.c:641:\tinput = *this_cpu_ptr(hyperv_pcpu_input_arg);\narch/x86/hyperv/ivm.c-642-\n--\narch/x86/hyperv/ivm.c=677=static int hv_mark_gpa_visibility(u16 count, const u64 pfn[],\n--\narch/x86/hyperv/ivm.c-702-\tlocal_irq_save(flags);\narch/x86/hyperv/ivm.c:703:\tinput = *this_cpu_ptr(hyperv_pcpu_input_arg);\narch/x86/hyperv/ivm.c-704-\n--\narch/x86/hyperv/mmu.c=59=static void hyperv_flush_tlb_multi(const struct cpumask *cpus,\n--\narch/x86/hyperv/mmu.c-74-\narch/x86/hyperv/mmu.c:75:\tflush = *this_cpu_ptr(hyperv_pcpu_input_arg);\narch/x86/hyperv/mmu.c-76-\n--\narch/x86/hyperv/mmu.c=172=static u64 hyperv_flush_tlb_others_ex(const struct cpumask *cpus,\n--\narch/x86/hyperv/mmu.c-181-\narch/x86/hyperv/mmu.c:182:\tflush = *this_cpu_ptr(hyperv_pcpu_input_arg);\narch/x86/hyperv/mmu.c-183-\n--\narch/x86/hyperv/nested.c=21=int hyperv_flush_guest_mapping(u64 as)\n--\narch/x86/hyperv/nested.c-32-\narch/x86/hyperv/nested.c:33:\tflush = *this_cpu_ptr(hyperv_pcpu_input_arg);\narch/x86/hyperv/nested.c-34-\n--\narch/x86/hyperv/nested.c=87=int hyperv_flush_guest_mapping_range(u64 as,\n--\narch/x86/hyperv/nested.c-100-\narch/x86/hyperv/nested.c:101:\tflush = *this_cpu_ptr(hyperv_pcpu_input_arg);\narch/x86/hyperv/nested.c-102-\n--\ndrivers/hv/hv.c=49=int hv_post_message(union hv_connection_id connection_id,\n--\ndrivers/hv/hv.c-64-\t * the comment in struct hv_per_cpu_context. A SNP VM with the paravisor\ndrivers/hv/hv.c:65:\t * can use the encrypted hyperv_pcpu_input_arg because it copies the\ndrivers/hv/hv.c-66-\t * input into the GHCB page, which has been decrypted by the paravisor.\n--\ndrivers/hv/hv.c-70-\telse\ndrivers/hv/hv.c:71:\t\taligned_msg = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/hv.c-72-\n--\ndrivers/hv/hv_balloon.c=1567=static int hv_free_page_report(struct page_reporting_dev_info *pr_dev_info,\n--\ndrivers/hv/hv_balloon.c-1578-\tlocal_irq_save(flags);\ndrivers/hv/hv_balloon.c:1579:\thint = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/hv_balloon.c-1580-\tif (!hint) {\n--\ndrivers/hv/hv_common.c=59=EXPORT_SYMBOL_GPL(hv_max_vp_index);\ndrivers/hv/hv_common.c-60-\ndrivers/hv/hv_common.c:61:void * __percpu *hyperv_pcpu_input_arg;\ndrivers/hv/hv_common.c:62:EXPORT_SYMBOL_GPL(hyperv_pcpu_input_arg);\ndrivers/hv/hv_common.c-63-\n--\ndrivers/hv/hv_common.c=87=void __init hv_common_free(void)\n--\ndrivers/hv/hv_common.c-100-\ndrivers/hv/hv_common.c:101:\tfree_percpu(hyperv_pcpu_input_arg);\ndrivers/hv/hv_common.c:102:\thyperv_pcpu_input_arg = NULL;\ndrivers/hv/hv_common.c-103-\n--\ndrivers/hv/hv_common.c=265=void __init hv_get_partition_id(void)\n--\ndrivers/hv/hv_common.c-271-\tlocal_irq_save(flags);\ndrivers/hv/hv_common.c:272:\toutput = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/hv_common.c-273-\tstatus = hv_do_hypercall(HVCALL_GET_PARTITION_ID, NULL, output);\n--\ndrivers/hv/hv_common.c=284=u8 __init get_vtl(void)\n--\ndrivers/hv/hv_common.c-292-\tlocal_irq_save(flags);\ndrivers/hv/hv_common.c:293:\tinput = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/hv_common.c-294-\toutput = *this_cpu_ptr(hyperv_pcpu_output_arg);\n--\ndrivers/hv/hv_common.c=315=int __init hv_common_init(void)\n--\ndrivers/hv/hv_common.c-372-\t */\ndrivers/hv/hv_common.c:373:\thyperv_pcpu_input_arg = alloc_percpu(void  *);\ndrivers/hv/hv_common.c:374:\tBUG_ON(!hyperv_pcpu_input_arg);\ndrivers/hv/hv_common.c-375-\n--\ndrivers/hv/hv_common.c=472=int hv_common_cpu_init(unsigned int cpu)\n--\ndrivers/hv/hv_common.c-484-\ndrivers/hv/hv_common.c:485:\tinputarg = (void **)this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/hv_common.c-486-\n--\ndrivers/hv/hv_common.c-511-\t\t * In a fully enlightened TDX/SNP VM with more than 64 VPs, if\ndrivers/hv/hv_common.c:512:\t\t * hyperv_pcpu_input_arg is not NULL, set_memory_decrypted() -\u003e\ndrivers/hv/hv_common.c-513-\t\t * ... -\u003e cpa_flush()-\u003e ... -\u003e __send_ipi_mask_ex() tries to\ndrivers/hv/hv_common.c:514:\t\t * use hyperv_pcpu_input_arg as the hypercall input page, which\ndrivers/hv/hv_common.c-515-\t\t * must be a decrypted page in such a VM, but the page is still\n--\ndrivers/hv/hv_common.c-517-\t\t * setting *inputarg after the above set_memory_decrypted(): if\ndrivers/hv/hv_common.c:518:\t\t * hyperv_pcpu_input_arg is NULL, __send_ipi_mask_ex() returns\ndrivers/hv/hv_common.c-519-\t\t * HV_STATUS_INVALID_PARAMETER immediately, and the function\n--\ndrivers/hv/hv_common.c=546=int hv_common_cpu_die(unsigned int cpu)\n--\ndrivers/hv/hv_common.c-549-\t/*\ndrivers/hv/hv_common.c:550:\t * The hyperv_pcpu_input_arg and hyperv_pcpu_output_arg memory\ndrivers/hv/hv_common.c:551:\t * is not freed when the CPU goes offline as the hyperv_pcpu_input_arg\ndrivers/hv/hv_common.c-552-\t * may be used by the Hyper-V vPCI driver in reassigning interrupts\n--\ndrivers/hv/hv_proc.c=19=int hv_call_deposit_pages(int node, u64 partition_id, u32 num_pages)\n--\ndrivers/hv/hv_proc.c-76-\ndrivers/hv/hv_proc.c:77:\tinput_page = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/hv_proc.c-78-\n--\ndrivers/hv/hv_proc.c=157=int hv_call_add_logical_proc(int node, u32 lp_index, u32 apic_id)\n--\ndrivers/hv/hv_proc.c-172-\ndrivers/hv/hv_proc.c:173:\t\tinput = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/hv_proc.c-174-\t\t/* We don't do anything with the output right now */\n--\ndrivers/hv/hv_proc.c=199=int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags)\n--\ndrivers/hv/hv_proc.c-216-\ndrivers/hv/hv_proc.c:217:\t\tinput = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/hv_proc.c-218-\n--\ndrivers/hv/hv_proc.c=243=int hv_call_notify_all_processors_started(void)\n--\ndrivers/hv/hv_proc.c-250-\tlocal_irq_save(irq_flags);\ndrivers/hv/hv_proc.c:251:\tinput = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/hv_proc.c-252-\tmemset(input, 0, sizeof(*input));\n--\ndrivers/hv/hv_proc.c=265=bool hv_lp_exists(u32 lp_index)\n--\ndrivers/hv/hv_proc.c-272-\tlocal_irq_save(flags);\ndrivers/hv/hv_proc.c:273:\tinput = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/hv_proc.c-274-\toutput = *this_cpu_ptr(hyperv_pcpu_output_arg);\n--\ndrivers/hv/hyperv_vmbus.h=126=struct hv_per_cpu_context {\n--\ndrivers/hv/hyperv_vmbus.h-150-\t * paravisor) to post a messages to Hyper-V: when such a VM calls\ndrivers/hv/hyperv_vmbus.h:151:\t * HVCALL_POST_MESSAGE, it can't use the hyperv_pcpu_input_arg (which\ndrivers/hv/hyperv_vmbus.h-152-\t * is encrypted in such a VM) as the hypercall input page, because\n--\ndrivers/hv/mshv_common.c=29=int hv_call_get_vp_registers(u32 vp_index, u64 partition_id, u16 count,\n--\ndrivers/hv/mshv_common.c-42-\ndrivers/hv/mshv_common.c:43:\tinput_page = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/mshv_common.c-44-\toutput_page = *this_cpu_ptr(hyperv_pcpu_output_arg);\n--\ndrivers/hv/mshv_common.c=75=int hv_call_set_vp_registers(u32 vp_index, u64 partition_id, u16 count,\n--\ndrivers/hv/mshv_common.c-86-\tlocal_irq_save(flags);\ndrivers/hv/mshv_common.c:87:\tinput_page = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/mshv_common.c-88-\n--\ndrivers/hv/mshv_common.c=116=int hv_call_get_partition_property(u64 partition_id,\n--\ndrivers/hv/mshv_common.c-125-\tlocal_irq_save(flags);\ndrivers/hv/mshv_common.c:126:\tinput = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/mshv_common.c-127-\toutput = *this_cpu_ptr(hyperv_pcpu_output_arg);\n--\ndrivers/hv/mshv_common.c=154=static int hv_initialize_sleep_states(void)\n--\ndrivers/hv/mshv_common.c-172-\tlocal_irq_save(flags);\ndrivers/hv/mshv_common.c:173:\tin = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/mshv_common.c-174-\tmemset(in, 0, sizeof(*in));\n--\ndrivers/hv/mshv_common.c=225=void hv_machine_power_off(void)\n--\ndrivers/hv/mshv_common.c-230-\tlocal_irq_save(flags);\ndrivers/hv/mshv_common.c:231:\tin = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/mshv_common.c-232-\tin-\u003esleep_state = HV_SLEEP_STATE_S5;\n--\ndrivers/hv/mshv_root_hv_call.c=42=int hv_call_withdraw_memory(u64 count, int node, u64 partition_id)\n--\ndrivers/hv/mshv_root_hv_call.c-59-\ndrivers/hv/mshv_root_hv_call.c:60:\t\tinput_page = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/mshv_root_hv_call.c-61-\n--\ndrivers/hv/mshv_root_hv_call.c=90=int hv_call_create_partition(u64 flags,\n--\ndrivers/hv/mshv_root_hv_call.c-102-\t\tlocal_irq_save(irq_flags);\ndrivers/hv/mshv_root_hv_call.c:103:\t\tinput = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/mshv_root_hv_call.c-104-\t\toutput = *this_cpu_ptr(hyperv_pcpu_output_arg);\n--\ndrivers/hv/mshv_root_hv_call.c=192=static int hv_do_map_gpa_hcall(u64 partition_id, u64 gfn, u64 page_struct_count,\n--\ndrivers/hv/mshv_root_hv_call.c-219-\t\tlocal_irq_save(irq_flags);\ndrivers/hv/mshv_root_hv_call.c:220:\t\tinput_page = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/mshv_root_hv_call.c-221-\n--\ndrivers/hv/mshv_root_hv_call.c=298=int hv_call_unmap_gpa_pages(u64 partition_id, u64 gfn, u64 page_count_4k,\n--\ndrivers/hv/mshv_root_hv_call.c-321-\t\tlocal_irq_save(irq_flags);\ndrivers/hv/mshv_root_hv_call.c:322:\t\tinput_page = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/mshv_root_hv_call.c-323-\n--\ndrivers/hv/mshv_root_hv_call.c=343=int hv_call_get_gpa_access_states(u64 partition_id, u32 count, u64 gpa_base_pfn,\n--\ndrivers/hv/mshv_root_hv_call.c-358-\t\tlocal_irq_save(flags);\ndrivers/hv/mshv_root_hv_call.c:359:\t\tinput_page = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/mshv_root_hv_call.c-360-\t\toutput_page = *this_cpu_ptr(hyperv_pcpu_output_arg);\n--\ndrivers/hv/mshv_root_hv_call.c=386=int hv_call_assert_virtual_interrupt(u64 partition_id, u32 vector,\n--\ndrivers/hv/mshv_root_hv_call.c-394-\tlocal_irq_save(flags);\ndrivers/hv/mshv_root_hv_call.c:395:\tinput = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/mshv_root_hv_call.c-396-\tmemset(input, 0, sizeof(*input));\n--\ndrivers/hv/mshv_root_hv_call.c=428=int hv_call_get_vp_state(u32 vp_index, u64 partition_id,\n--\ndrivers/hv/mshv_root_hv_call.c-449-\t\tlocal_irq_save(flags);\ndrivers/hv/mshv_root_hv_call.c:450:\t\tinput = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/mshv_root_hv_call.c-451-\t\toutput = *this_cpu_ptr(hyperv_pcpu_output_arg);\n--\ndrivers/hv/mshv_root_hv_call.c=482=int hv_call_set_vp_state(u32 vp_index, u64 partition_id,\n--\ndrivers/hv/mshv_root_hv_call.c-509-\t\tlocal_irq_save(flags);\ndrivers/hv/mshv_root_hv_call.c:510:\t\tinput = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/mshv_root_hv_call.c-511-\t\tmemset(input, 0, sizeof(*input));\n--\ndrivers/hv/mshv_root_hv_call.c=541=static int hv_call_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type,\n--\ndrivers/hv/mshv_root_hv_call.c-553-\ndrivers/hv/mshv_root_hv_call.c:554:\t\tinput = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/mshv_root_hv_call.c-555-\t\toutput = *this_cpu_ptr(hyperv_pcpu_output_arg);\n--\ndrivers/hv/mshv_root_hv_call.c=624=static int hv_call_unmap_vp_state_page(u64 partition_id, u32 vp_index, u32 type,\n--\ndrivers/hv/mshv_root_hv_call.c-632-\ndrivers/hv/mshv_root_hv_call.c:633:\tinput = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/mshv_root_hv_call.c-634-\n--\ndrivers/hv/mshv_root_hv_call.c=660=int hv_call_get_partition_property_ex(u64 partition_id, u64 property_code,\n--\ndrivers/hv/mshv_root_hv_call.c-669-\tlocal_irq_save(flags);\ndrivers/hv/mshv_root_hv_call.c:670:\tinput = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/mshv_root_hv_call.c-671-\toutput = *this_cpu_ptr(hyperv_pcpu_output_arg);\n--\ndrivers/hv/mshv_root_hv_call.c=703=hv_call_create_port(u64 port_partition_id, union hv_port_id port_id,\n--\ndrivers/hv/mshv_root_hv_call.c-714-\t\tlocal_irq_save(flags);\ndrivers/hv/mshv_root_hv_call.c:715:\t\tinput = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/mshv_root_hv_call.c-716-\t\tmemset(input, 0, sizeof(*input));\n--\ndrivers/hv/mshv_root_hv_call.c=756=hv_call_connect_port(u64 port_partition_id, union hv_port_id port_id,\n--\ndrivers/hv/mshv_root_hv_call.c-767-\t\tlocal_irq_save(flags);\ndrivers/hv/mshv_root_hv_call.c:768:\t\tinput = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/mshv_root_hv_call.c-769-\t\tmemset(input, 0, sizeof(*input));\n--\ndrivers/hv/mshv_root_hv_call.c=830=static int hv_call_map_stats_page2(enum hv_stats_object_type type,\n--\ndrivers/hv/mshv_root_hv_call.c-843-\t\tlocal_irq_save(flags);\ndrivers/hv/mshv_root_hv_call.c:844:\t\tinput = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/mshv_root_hv_call.c-845-\n--\ndrivers/hv/mshv_root_hv_call.c=900=hv_call_map_stats_page(enum hv_stats_object_type type,\n--\ndrivers/hv/mshv_root_hv_call.c-911-\t\tlocal_irq_save(flags);\ndrivers/hv/mshv_root_hv_call.c:912:\t\tinput = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/mshv_root_hv_call.c-913-\t\toutput = *this_cpu_ptr(hyperv_pcpu_output_arg);\n--\ndrivers/hv/mshv_root_hv_call.c=978=static int hv_call_unmap_stats_page(enum hv_stats_object_type type,\n--\ndrivers/hv/mshv_root_hv_call.c-985-\tlocal_irq_save(flags);\ndrivers/hv/mshv_root_hv_call.c:986:\tinput = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/mshv_root_hv_call.c-987-\n--\ndrivers/hv/mshv_root_hv_call.c=1012=int hv_call_modify_spa_host_access(u64 partition_id, struct page **pages,\n--\ndrivers/hv/mshv_root_hv_call.c-1039-\t\tlocal_irq_save(irq_flags);\ndrivers/hv/mshv_root_hv_call.c:1040:\t\tinput_page = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/mshv_root_hv_call.c-1041-\n--\ndrivers/hv/mshv_root_main.c=2200=static int __init hv_retrieve_scheduler_type(enum hv_scheduler_type *out)\n--\ndrivers/hv/mshv_root_main.c-2207-\tlocal_irq_save(flags);\ndrivers/hv/mshv_root_main.c:2208:\tinput = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/hv/mshv_root_main.c-2209-\toutput = *this_cpu_ptr(hyperv_pcpu_output_arg);\n--\ndrivers/hv/mshv_vtl_main.c=1117=static int mshv_vtl_hvcall_call(struct mshv_vtl_hvcall_fd *fd,\n--\ndrivers/hv/mshv_vtl_main.c-1144-\t * This may create a problem for Confidential VM (CVM) usecase where we need to use\ndrivers/hv/mshv_vtl_main.c:1145:\t * Hyper-V driver allocated per-cpu input and output pages (hyperv_pcpu_input_arg and\ndrivers/hv/mshv_vtl_main.c-1146-\t * hyperv_pcpu_output_arg) for making a hypervisor call.\n--\ndrivers/pci/controller/pci-hyperv.c=610=static void hv_irq_retarget_interrupt(struct irq_data *data)\n--\ndrivers/pci/controller/pci-hyperv.c-637-\ndrivers/pci/controller/pci-hyperv.c:638:\tparams = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/pci/controller/pci-hyperv.c-639-\tmemset(params, 0, sizeof(*params));\n--\ndrivers/pci/controller/pci-hyperv.c=1096=static void hv_pci_read_mmio(struct device *dev, phys_addr_t gpa, int size, u32 *val)\n--\ndrivers/pci/controller/pci-hyperv.c-1106-\t */\ndrivers/pci/controller/pci-hyperv.c:1107:\tin = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/pci/controller/pci-hyperv.c:1108:\tout = *this_cpu_ptr(hyperv_pcpu_input_arg) + sizeof(*in);\ndrivers/pci/controller/pci-hyperv.c-1109-\tin-\u003egpa = gpa;\n--\ndrivers/pci/controller/pci-hyperv.c=1130=static void hv_pci_write_mmio(struct device *dev, phys_addr_t gpa, int size, u32 val)\n--\ndrivers/pci/controller/pci-hyperv.c-1138-\t */\ndrivers/pci/controller/pci-hyperv.c:1139:\tin = *this_cpu_ptr(hyperv_pcpu_input_arg);\ndrivers/pci/controller/pci-hyperv.c-1140-\tin-\u003egpa = gpa;\n--\ninclude/asm-generic/mshyperv.h=71=extern enum hv_partition_type hv_curr_partition_type;\ninclude/asm-generic/mshyperv.h-72-\ninclude/asm-generic/mshyperv.h:73:extern void * __percpu *hyperv_pcpu_input_arg;\ninclude/asm-generic/mshyperv.h-74-extern void * __percpu *hyperv_pcpu_output_arg;\n"
}

123/3 2026/09/26 15:35 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 96650
							output: 43
							thoughts: 122
						

Thoughts:
Okay, let's break this down.

**Observing Hyper-V MMIO Access and IPI Handling**

Alright, I'm looking at some interesting code here, specifically within `hv_pci_read_mmio` and `hv_pci_write_mmio`. Immediately, I see the access to `hyperv_pcpu_input_arg`. This is crucial. It looks like we're using `this_cpu_ptr` to get a per-CPU pointer, which is standard for performance optimization in this context. We grab a pointer to `in`, and then calculate an offset for `out` based on the size of the `in` struct. Then, we populate `in->gpa` which strongly suggests we are dealing with Guest Physical Address translation here. This is classic Hyper-V interaction. It’s likely how we facilitate direct memory access (DMA) between the guest and the host, probably through a Hyper-V specific protocol. The use of per-CPU storage hints at parallel processing and the need to maintain context on a per-CPU basis for these MMIO operations.

Then, my attention is drawn to the call to `__send_ipi_mask_ex`. Now, I'm eager to understand this IPI function. I need to understand what this does, and how it is being used to coordinate across CPUs or even potentially trigger a response to this MMIO access! It could be signaling the host, or another guest vCPU about a pending MMIO transaction. This is a very common approach in virtualized environments. I'll need to dig deeper into the code of `__send_ipi_mask_ex` to understand its role within the larger framework.



124/3 2026/09/26 15:35 tool read-file
0m Args:
{
  "File": "arch/x86/hyperv/hv_apic.c",
  "FirstLine": 110,
  "LineCount": 45
}

Results:
{
  "Contents": " 110:\tstatic bool __send_ipi_mask_ex(const struct cpumask *mask, int vector,\n 111:\t\t\t\t       bool exclude_self)\n 112:\t{\n 113:\t\tstruct hv_send_ipi_ex *ipi_arg;\n 114:\t\tunsigned long flags;\n 115:\t\tint nr_bank = 0;\n 116:\t\tu64 status = HV_STATUS_INVALID_PARAMETER;\n 117:\t\n 118:\t\tif (!(ms_hyperv.hints \u0026 HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED))\n 119:\t\t\treturn false;\n 120:\t\n 121:\t\tlocal_irq_save(flags);\n 122:\t\tipi_arg = *this_cpu_ptr(hyperv_pcpu_input_arg);\n 123:\t\n 124:\t\tif (unlikely(!ipi_arg))\n 125:\t\t\tgoto ipi_mask_ex_done;\n 126:\t\n 127:\t\tipi_arg-\u003evector = vector;\n 128:\t\tipi_arg-\u003ereserved = 0;\n 129:\t\tipi_arg-\u003evp_set.valid_bank_mask = 0;\n 130:\t\n 131:\t\t/*\n 132:\t\t * Use HV_GENERIC_SET_ALL and avoid converting cpumask to VP_SET\n 133:\t\t * when the IPI is sent to all currently present CPUs.\n 134:\t\t */\n 135:\t\tif (!cpumask_equal(mask, cpu_present_mask) || exclude_self) {\n 136:\t\t\tipi_arg-\u003evp_set.format = HV_GENERIC_SET_SPARSE_4K;\n 137:\t\n 138:\t\t\tnr_bank = cpumask_to_vpset_skip(\u0026ipi_arg-\u003evp_set, mask,\n 139:\t\t\t\t\t\t\texclude_self ? cpu_is_self : NULL);\n 140:\t\n 141:\t\t\t/*\n 142:\t\t\t * 'nr_bank \u003c= 0' means some CPUs in cpumask can't be\n 143:\t\t\t * represented in VP_SET. Return an error and fall back to\n 144:\t\t\t * native (architectural) method of sending IPIs.\n 145:\t\t\t */\n 146:\t\t\tif (nr_bank \u003c= 0)\n 147:\t\t\t\tgoto ipi_mask_ex_done;\n 148:\t\t} else {\n 149:\t\t\tipi_arg-\u003evp_set.format = HV_GENERIC_SET_ALL;\n 150:\t\t}\n 151:\t\n 152:\t\t/*\n 153:\t\t * For this hypercall, Hyper-V treats the valid_bank_mask field\n 154:\t\t * of ipi_arg-\u003evp_set as part of the fixed size input header.\n"
}

125/3 2026/09/26 15:35 llm kmsan-evaluator-compressor
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Reply:
### State and Execution History Summary

#### 1. Approaches Tried and Results
- **Overall Patch Objective**: Analyzed patch series introducing confidential computing shared memory management (`linux/cc_shared.h`, `mm/cc_shared.c`) across DMA subsystems (`dma-mapping`, `dma-direct`, `swiotlb`, `contiguous`, `pool`), `system_heap`, `arm-gic-v3`, and architecture-specific memory encryption/decryption hooks (`arm64`, `s390`, `powerpc`, `x86/coco`, `x86/hyperv`, `pkvm-guest`).
- **Memory Zeroing in Transition Functions**:
  - Examined changes in `arch/arm64/mm/pageattr.c`, `arch/powerpc/platforms/pseries/svm.c`, `arch/s390/mm/init.c`, `arch/x86/coco/tdx/tdx.c`, `arch/x86/hyperv/ivm.c`, `arch/x86/mm/mem_encrypt_amd.c`, and `drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c`.
  - Found that these architectures add explicit `memset(..., 0, ...)` when transitioning memory to decrypted/shared state (`!enc`).
- **`system_heap` Investigation (`drivers/dma-buf/heaps/system_heap.c`)**:
  - Verified allocation and zeroing behavior. Default allocations use `LOW_ORDER_GFP` (`GFP_HIGHUSER | __GFP_ZERO`) or `HIGH_ORDER_GFP` (`... | __GFP_ZERO ...`).
  - For `cc_shared` buffers, `alloc_cc_shared_pages()` is invoked, which calls `__alloc_cc_shared_pages_node()` where `gfp &= ~(__GFP_HIGHMEM | __GFP_ZERO)`. The memory is cleared during `cc_make_shared()` -> `set_memory_decrypted()`, which zeroes memory on all confidential computing platforms.
  - Checked `system_heap_mmap()`: pages mapped to user space are properly zeroed.
- **DMA Direct / Contiguous / Pool Investigation**:
  - Investigated `dma_direct_alloc()` and `dma_direct_alloc_pages()` in `kernel/dma/direct.c`.
  - In `dma_direct_alloc()`, allocations strip `__GFP_ZERO`. If `!mark_mem_decrypt`, it explicitly calls `memset(cpu_addr, 0, size)`. If `mark_mem_decrypt`, it calls `dma_set_decrypted()` -> `cc_make_shared()`, which zeroes the memory in platform transition handlers when encryption is enabled.
  - Traced `force_dma_unencrypted(dev)` on x86 (`arch/x86/mm/mem_encrypt.c`). It only returns true if `CC_ATTR_GUEST_MEM_ENCRYPT` or `CC_ATTR_HOST_MEM_ENCRYPT` is active.
- **Hyper-V Driver Investigation (`drivers/hv/`)**:
  - Examined `drivers/hv/connection.c`, `drivers/hv/hv_common.c`, `arch/x86/hyperv/hv_init.c`, and `drivers/hv/hv.c`.
  - Identified critical discrepancy in `drivers/hv/hv.c:hv_alloc_page()`:
    - Pre-patch:
      ```c
      if (decrypt)
          ret = set_memory_decrypted((unsigned long)*page, 1);
      if (ret)
          goto failed;
      memset(*page, 0, PAGE_SIZE);
      ```
    - Post-patch:
      ```c
      if (decrypt) {
          ret = set_memory_decrypted((unsigned long)*page, 1);
          if (ret)
              goto failed;
      } else {
          memset(*page, 0, PAGE_SIZE);
      }
      ```
    - In `hv_synic_alloc()`:
      `const bool decrypt = !vmbus_is_confidential();`
    - In standard non-confidential Hyper-V guest VMs (normal Linux on Hyper-V), `vmbus_is_confidential()` returns `false`, so `decrypt` is `true`.
    - For non-confidential VMs, `cc_platform_has(CC_ATTR_MEM_ENCRYPT)` is `false`. Therefore `set_memory_decrypted()` calls `__set_memory_enc_dec()`, which immediately returns 0 without zeroing memory.
    - Result: `hv_alloc_page()` allocates a page via `__get_free_page(GFP_KERNEL)` (uninitialized heap/page allocation), calls `set_memory_decrypted()` (which is a no-op), and *skips* `memset(*page, 0, PAGE_SIZE)`.
    - Pages affected include `hyp_synic_message_page` and `hyp_synic_event_page`.
    - Traced usage to `__vmbus_on_msg_dpc()` and `vmbus_message_sched()` in `drivers/hv/vmbus_drv.c`:
      `__vmbus_on_msg_dpc()` reads `msg = (struct hv_message *)message_page_addr + VMBUS_MESSAGE_SINT;`, copies it to stack `memcpy(&msg_copy, msg, sizeof(struct hv_message));`, and evaluates branching on `msg_copy.header.message_type` (`if (message_type == HVMSG_NONE) return;`), which will execute based on uninitialized memory read from the page allocator!

#### 2. Current Hypotheses and Active Lines of Investigation
- **Uninitialized Memory Bug in Hyper-V SynIC**:
  - The patch introduces an uninitialized memory condition: `hv_alloc_page()` fails to zero memory when `decrypt == true` on systems where confidential computing/guest memory encryption is disabled.
  - This leads to kernel execution and branching on uninitialized page contents in `vmbus_drv.c:__vmbus_on_msg_dpc()`.
- **KMSAN vs KASAN Applicability**:
  - KASAN detects out-of-bounds, use-after-free, double-free. It does NOT detect reads of uninitialized memory or control-flow branching based on uninitialized memory.
  - KMSAN specifically tracks uninitialized memory origin and detects reads / branching on uninitialized memory.
  - Since this bug involves reading uninitialized page allocations and using them in conditional logic (`if (message_type == HVMSG_NONE)` and dispatching handlers), KMSAN is specifically and uniquely equipped to detect this vulnerability.
  - Therefore, `NeedsKMSAN=true` is strongly indicated.

#### 3. Critical Files, Functions, and Code Snippets

- `drivers/hv/hv.c`:
```c
static int hv_alloc_page(void **page, bool decrypt, const char *note)
{
	int ret = 0;

	*page = (void *)__get_free_page(GFP_KERNEL);
	if (!*page)
		return -ENOMEM;

	if (decrypt) {
		ret = set_memory_decrypted((unsigned long)*page, 1);
		if (ret)
			goto failed;
	} else {
		memset(*page, 0, PAGE_SIZE);
	}
	return 0;

failed:
...
```

- `drivers/hv/hv.c`:
```c
int hv_synic_alloc(void)
{
...
	const bool decrypt = !vmbus_is_confidential();
...
	if (!ms_hyperv.paravisor_present && !hv_root_partition()) {
		ret = hv_alloc_page(&hv_cpu->hyp_synic_message_page,
			decrypt, "hypervisor SynIC msg");
		if (ret)
			goto err;
		ret = hv_alloc_page(&hv_cpu->hyp_synic_event_page,
			decrypt, "hypervisor SynIC event");
...
```

- `arch/x86/mm/pat/set_memory.c`:
```c
static int __set_memory_enc_dec(unsigned long addr, int numpages, bool enc)
{
	int ret = 0;

	if (cc_platform_has(CC_ATTR_MEM_ENCRYPT)) {
		if (!down_read_trylock(&mem_enc_lock))
			return -EBUSY;

		ret = __set_memory_enc_pgtable(addr, numpages, enc);

		up_read(&mem_enc_lock);
	}

	return ret;
}
```

- `drivers/hv/vmbus_drv.c`:
```c
static void __vmbus_on_msg_dpc(void *message_page_addr)
{
...
	msg = (struct hv_message *)message_page_addr + VMBUS_MESSAGE_SINT;
	memcpy(&msg_copy, msg, sizeof(struct hv_message));

	message_type = msg_copy.header.message_type;
	if (message_type == HVMSG_NONE)
		/* no msg */
		return;
...
```

#### 4. Watchouts
- Avoid re-grepping the entire kernel for generic DMA attributes or re-reading files that were already fully vetted (`system_heap.c`, `direct.c`).
- Analysis of the bug in `drivers/hv/hv.c` is complete and fully traced from allocation through page decryption no-op to uninitialized memory use in DPC message processing.
- The `set-results` tool must be invoked exactly once in the next turn with `NeedsKMSAN=true` and detailed justification contrasting KASAN vs KMSAN before providing the final reply.

Tokens:
							input: 67235
							output: 2260
							thoughts: 0
						

126/3 2026/09/26 15:35 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 45457
							output: 40
							thoughts: 0
						

127/3 2026/09/26 15:35 tool read-file
0m Args:
{
  "File": "arch/x86/hyperv/mmu.c",
  "FirstLine": 70,
  "LineCount": 40
}

Results:
{
  "Contents": "  70:\t\tif (!hv_hypercall_pg)\n  71:\t\t\tgoto do_native;\n  72:\t\n  73:\t\tlocal_irq_save(flags);\n  74:\t\n  75:\t\tflush = *this_cpu_ptr(hyperv_pcpu_input_arg);\n  76:\t\n  77:\t\tif (unlikely(!flush)) {\n  78:\t\t\tlocal_irq_restore(flags);\n  79:\t\t\tgoto do_native;\n  80:\t\t}\n  81:\t\n  82:\t\tif (info-\u003emm) {\n  83:\t\t\t/*\n  84:\t\t\t * AddressSpace argument must match the CR3 with PCID bits\n  85:\t\t\t * stripped out.\n  86:\t\t\t */\n  87:\t\t\tflush-\u003eaddress_space = virt_to_phys(info-\u003emm-\u003epgd);\n  88:\t\t\tflush-\u003eaddress_space \u0026= CR3_ADDR_MASK;\n  89:\t\t\tflush-\u003eflags = 0;\n  90:\t\t} else {\n  91:\t\t\tflush-\u003eaddress_space = 0;\n  92:\t\t\tflush-\u003eflags = HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES;\n  93:\t\t}\n  94:\t\n  95:\t\tflush-\u003eprocessor_mask = 0;\n  96:\t\tif (cpumask_equal(cpus, cpu_present_mask)) {\n  97:\t\t\tflush-\u003eflags |= HV_FLUSH_ALL_PROCESSORS;\n  98:\t\t} else {\n  99:\t\t\t/*\n 100:\t\t\t * From the supplied CPU set we need to figure out if we can get\n 101:\t\t\t * away with cheaper HVCALL_FLUSH_VIRTUAL_ADDRESS_{LIST,SPACE}\n 102:\t\t\t * hypercalls. This is possible when the highest VP number in\n 103:\t\t\t * the set is \u003c 64. As VP numbers are usually in ascending order\n 104:\t\t\t * and match Linux CPU ids, here is an optimization: we check\n 105:\t\t\t * the VP number for the highest bit in the supplied set first\n 106:\t\t\t * so we can quickly find out if using *_EX hypercalls is a\n 107:\t\t\t * must. We will also check all VP numbers when walking the\n 108:\t\t\t * supplied CPU set to remain correct in all cases.\n 109:\t\t\t */\n"
}

128/3 2026/09/26 15:35 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 46101
							output: 35
							thoughts: 0
						

129/3 2026/09/26 15:35 tool read-file
0m Args:
{
  "File": "drivers/hv/connection.c",
  "FirstLine": 260,
  "LineCount": 45
}

Results:
{
  "Contents": " 260:\t\tvmbus_connection.monitor_pages[1] = (void *)__get_free_page(GFP_KERNEL);\n 261:\t\tif ((vmbus_connection.monitor_pages[0] == NULL) ||\n 262:\t\t    (vmbus_connection.monitor_pages[1] == NULL)) {\n 263:\t\t\tret = -ENOMEM;\n 264:\t\t\tgoto cleanup;\n 265:\t\t}\n 266:\t\n 267:\t\tif (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) {\n 268:\t\t\tret = set_memory_decrypted((unsigned long)vmbus_connection.monitor_pages[0],\n 269:\t\t\t\t\t\t   1);\n 270:\t\t\tret |= set_memory_decrypted((unsigned long)vmbus_connection.monitor_pages[1],\n 271:\t\t\t\t\t\t    1);\n 272:\t\t\tif (ret) {\n 273:\t\t\t\t/*\n 274:\t\t\t\t * If set_memory_decrypted() fails, the encryption state\n 275:\t\t\t\t * of the memory is unknown. So leak the memory instead\n 276:\t\t\t\t * of risking returning decrypted memory to the free list.\n 277:\t\t\t\t * For simplicity, always handle both pages the same.\n 278:\t\t\t\t */\n 279:\t\t\t\tvmbus_connection.monitor_pages[0] = NULL;\n 280:\t\t\t\tvmbus_connection.monitor_pages[1] = NULL;\n 281:\t\t\t\tgoto cleanup;\n 282:\t\t\t}\n 283:\t\t} else {\n 284:\t\t\tmemset(vmbus_connection.monitor_pages[0], 0, HV_HYP_PAGE_SIZE);\n 285:\t\t\tmemset(vmbus_connection.monitor_pages[1], 0, HV_HYP_PAGE_SIZE);\n 286:\t\t}\n 287:\t\n 288:\t\tmsginfo = kzalloc(sizeof(*msginfo) +\n 289:\t\t\t\t  sizeof(struct vmbus_channel_initiate_contact),\n 290:\t\t\t\t  GFP_KERNEL);\n 291:\t\tif (msginfo == NULL) {\n 292:\t\t\tret = -ENOMEM;\n 293:\t\t\tgoto cleanup;\n 294:\t\t}\n 295:\t\n 296:\t\t/*\n 297:\t\t * Negotiate a compatible VMBUS version number with the\n 298:\t\t * host. We start with the highest number we can support\n 299:\t\t * and work our way down until we negotiate a compatible\n 300:\t\t * version.\n 301:\t\t */\n 302:\t\n 303:\t\tfor (i = 0; ; i++) {\n 304:\t\t\tif (i == ARRAY_SIZE(vmbus_versions)) {\n"
}

130/3 2026/09/26 15:35 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 46875
							output: 23
							thoughts: 1876
						

Thoughts:
**Critical Vulnerability Analysis: Uninitialized Memory Exposure in Hyper-V Driver Patch**

Alright, let's break this down. This patch introduces a potentially severe vulnerability within the Hyper-V driver by incorrectly handling memory initialization, specifically in the `hv_alloc_page()` and `hv_common_cpu_init()` functions.

First, let's focus on `hv_alloc_page()`.  I've traced the logic, and it's clear:  when `decrypt` is true (which it *always* is for standard non-confidential VMs via `hv_synic_alloc()`), the patch *removes* the `memset` that zeroed the allocated page.  Instead, it now conditionally calls `set_memory_decrypted()`, which, *on standard hardware configurations (non-confidential VMs)*, effectively does *nothing*. The page comes from `__get_free_page()`, which, without `init_on_alloc` set (and it's not in standard builds), returns *uninitialized* memory.

This means that `hyp_synic_message_page` and `hyp_synic_event_page` – memory used for critical Hyper-V communication – are now filled with garbage data.  In `__vmbus_on_msg_dpc()`, the code copies a message, and if the `message_type` is non-zero (due to uninitialized data), it starts interpreting *garbage* as message content. This leads to branch based on uninitialized data, accessing function pointers from junk memory, potentially leading to arbitrary code execution, and at a minimum, unpredictable behavior. The same applies to event handling, where flags from uninitialized memory are tested.

Now, let's shift to `hv_common_cpu_init()`. This is another point of concern. Here, the patch modifies a block of code dealing with SNP/TDX environments.  The original code *always* zeroed the memory allocated by `kmalloc_array()`. The patch now conditionally zero the memory depending on if isolation is active.  *This changes the behavior in non-SNP/TDX modes.*  While callers of `hyperv_pcpu_input_arg` typically `memset` the memory (but not all!), the critical flaw is in the zeroing of `hyp_synic_message_page` and `hyp_synic_event_page` - a regression that has been introduced, meaning that the allocated memory is not cleared, as it was previously.

The fundamental mistake is the assumption that `set_memory_decrypted()` *always* zeroes the memory, which is *incorrect* for standard, non-confidential VM configurations.  The patch author removed the `memset` without considering that in most cases, `set_memory_decrypted()` is a no-op!

This represents a severe security vulnerability. Uninitialized memory being used as control flow or message data is a classic recipe for exploitation.  This patch *must* be reverted immediately, and the code needs a thorough security audit. The `memset` calls, which are critical for security in standard VM deployments, are the correct behavior, and must be restored and the conditional around the `memset` in `hv_alloc_page` must be removed to avoid unintended side effects. The correct approach is to always zero the allocated pages.



131/3 2026/09/26 15:36 tool grepper
0m Args:
{
  "Expression": "bool vmbus_is_confidential"
}

Results:
{
  "Output": "drivers/hv/hyperv_vmbus.h=354=extern const struct vmbus_channel_message_table_entry\n--\ndrivers/hv/hyperv_vmbus.h-359-\ndrivers/hv/hyperv_vmbus.h:360:bool vmbus_is_confidential(void);\ndrivers/hv/hyperv_vmbus.h-361-\n--\ndrivers/hv/vmbus_drv.c=64=static bool is_confidential;\ndrivers/hv/vmbus_drv.c-65-\ndrivers/hv/vmbus_drv.c:66:bool vmbus_is_confidential(void)\ndrivers/hv/vmbus_drv.c-67-{\n"
}

132/3 2026/09/26 15:36 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 49679
							output: 38
							thoughts: 0
						

133/3 2026/09/26 15:36 tool read-file
0m Args:
{
  "File": "drivers/hv/vmbus_drv.c",
  "FirstLine": 64,
  "LineCount": 20
}

Results:
{
  "Contents": "  64:\tstatic bool is_confidential;\n  65:\t\n  66:\tbool vmbus_is_confidential(void)\n  67:\t{\n  68:\t\treturn is_confidential;\n  69:\t}\n  70:\tEXPORT_SYMBOL_GPL(vmbus_is_confidential);\n  71:\t\n  72:\tstatic bool skip_vmbus_unload;\n  73:\t\n  74:\t/*\n  75:\t * Allow a VMBus framebuffer driver to specify that in the case of a panic,\n  76:\t * it will do the VMbus unload operation once it has flushed any dirty\n  77:\t * portions of the framebuffer to the Hyper-V host.\n  78:\t */\n  79:\tvoid vmbus_set_skip_unload(bool skip)\n  80:\t{\n  81:\t\tskip_vmbus_unload = skip;\n  82:\t}\n  83:\tEXPORT_SYMBOL_GPL(vmbus_set_skip_unload);\n"
}

134/3 2026/09/26 15:36 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 49993
							output: 28
							thoughts: 31
						

Thoughts:
Here's my thought process, as if I were considering this myself:

**Tracing `is_confidential` in `vmbus_drv.c`**

Okay, so I need to figure out where the `is_confidential` flag is being set within `vmbus_drv.c`. That's the obvious first step. Best to go straight to the source and see where this boolean variable is getting its initial value or being manipulated. I'll reach for `grep` immediately. It's the most efficient way to pinpoint all instances of the identifier and, hopefully, quickly understand its context within the code. Let's get to it.



135/3 2026/09/26 15:36 tool grepper
0m Args:
{
  "Expression": "is_confidential",
  "PathPrefix": "drivers/hv/"
}

Results:
{
  "Output": "drivers/hv/channel_mgmt.c=1027=static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)\n--\ndrivers/hv/channel_mgmt.c-1052-\tif (co_ring_buffer || co_external_memory) {\ndrivers/hv/channel_mgmt.c:1053:\t\tif (vmbus_proto_version \u003c VERSION_WIN10_V6_0 || !vmbus_is_confidential()) {\ndrivers/hv/channel_mgmt.c-1054-\t\t\tpr_err(\"Invalid offer relid=%d: no support for confidential VMBus\\n\",\n--\ndrivers/hv/connection.c=76=static int vmbus_try_connection_id(struct vmbus_channel_msginfo *msginfo,\n--\ndrivers/hv/connection.c-110-\ndrivers/hv/connection.c:111:\tif (vmbus_is_confidential() \u0026\u0026 version \u003e= VERSION_WIN10_V6_0)\ndrivers/hv/connection.c-112-\t\tmsg-\u003efeature_flags = VMBUS_FEATURE_FLAG_CONFIDENTIAL_CHANNELS;\n--\ndrivers/hv/hv.c=49=int hv_post_message(union hv_connection_id connection_id,\n--\ndrivers/hv/hv.c-78-\ndrivers/hv/hv.c:79:\tif (ms_hyperv.paravisor_present \u0026\u0026 !vmbus_is_confidential()) {\ndrivers/hv/hv.c-80-\t\t/*\n--\ndrivers/hv/hv.c=171=int hv_synic_alloc(void)\n--\ndrivers/hv/hv.c-174-\tstruct hv_per_cpu_context *hv_cpu;\ndrivers/hv/hv.c:175:\tconst bool decrypt = !vmbus_is_confidential();\ndrivers/hv/hv.c-176-\n--\ndrivers/hv/hv.c-221-\ndrivers/hv/hv.c:222:\t\tif (vmbus_is_confidential()) {\ndrivers/hv/hv.c-223-\t\t\tret = hv_alloc_page(\u0026hv_cpu-\u003epara_synic_message_page,\n--\ndrivers/hv/hv.c=244=void hv_synic_free(void)\n--\ndrivers/hv/hv.c-246-\tint cpu;\ndrivers/hv/hv.c:247:\tconst bool encrypt = !vmbus_is_confidential();\ndrivers/hv/hv.c-248-\n--\ndrivers/hv/hv.c-261-\t\t}\ndrivers/hv/hv.c:262:\t\tif (vmbus_is_confidential()) {\ndrivers/hv/hv.c-263-\t\t\thv_free_page(\u0026hv_cpu-\u003epara_synic_event_page,\n--\ndrivers/hv/hv.c=381=int hv_synic_init(unsigned int cpu)\ndrivers/hv/hv.c-382-{\ndrivers/hv/hv.c:383:\tif (vmbus_is_confidential())\ndrivers/hv/hv.c-384-\t\thv_para_synic_enable_regs(cpu);\n--\ndrivers/hv/hv.c-397-\thv_hyp_synic_enable_regs(cpu);\ndrivers/hv/hv.c:398:\tif (vmbus_is_confidential())\ndrivers/hv/hv.c-399-\t\thv_para_synic_enable_interrupts();\n--\ndrivers/hv/hv.c=579=int hv_synic_cleanup(unsigned int cpu)\n--\ndrivers/hv/hv.c-638-\thv_hyp_synic_disable_regs(cpu);\ndrivers/hv/hv.c:639:\tif (!vmbus_is_confidential())\ndrivers/hv/hv.c-640-\t\thv_hyp_synic_disable_interrupts();\n--\ndrivers/hv/hv.c-646-\t */\ndrivers/hv/hv.c:647:\tif (vmbus_is_confidential()) {\ndrivers/hv/hv.c-648-\t\thv_para_synic_disable_regs(cpu);\n--\ndrivers/hv/hyperv_vmbus.h=354=extern const struct vmbus_channel_message_table_entry\n--\ndrivers/hv/hyperv_vmbus.h-359-\ndrivers/hv/hyperv_vmbus.h:360:bool vmbus_is_confidential(void);\ndrivers/hv/hyperv_vmbus.h-361-\n--\ndrivers/hv/hyperv_vmbus.h=364=static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type)\n--\ndrivers/hv/hyperv_vmbus.h-393-\t\t */\ndrivers/hv/hyperv_vmbus.h:394:\t\tif (vmbus_is_confidential())\ndrivers/hv/hyperv_vmbus.h-395-\t\t\thv_para_set_synic_register(HV_MSR_EOM, 0);\n--\ndrivers/hv/vmbus_drv.c=58=int vmbus_interrupt;\n--\ndrivers/hv/vmbus_drv.c-63- */\ndrivers/hv/vmbus_drv.c:64:static bool is_confidential;\ndrivers/hv/vmbus_drv.c-65-\ndrivers/hv/vmbus_drv.c:66:bool vmbus_is_confidential(void)\ndrivers/hv/vmbus_drv.c-67-{\ndrivers/hv/vmbus_drv.c:68:\treturn is_confidential;\ndrivers/hv/vmbus_drv.c-69-}\ndrivers/hv/vmbus_drv.c:70:EXPORT_SYMBOL_GPL(vmbus_is_confidential);\ndrivers/hv/vmbus_drv.c-71-\n--\ndrivers/hv/vmbus_drv.c=1469=static int vmbus_bus_init(void)\n--\ndrivers/hv/vmbus_drv.c-1513-\t */\ndrivers/hv/vmbus_drv.c:1514:\tis_confidential = ms_hyperv.confidential_vmbus_available;\ndrivers/hv/vmbus_drv.c:1515:\tif (is_confidential)\ndrivers/hv/vmbus_drv.c-1516-\t\tpr_info(\"Establishing connection to the confidential VMBus\\n\");\ndrivers/hv/vmbus_drv.c:1517:\thv_para_set_sint_proxy(!is_confidential);\ndrivers/hv/vmbus_drv.c-1518-\tret = vmbus_alloc_synic_and_connect();\n"
}

136/3 2026/09/26 15:36 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 51946
							output: 21
							thoughts: 2495
						

Thoughts:
**Uninitialized Memory Vulnerability in Hyper-V SynIC Allocation**

Right, let me break this down. My analysis centers on a potential vulnerability in Hyper-V's SynIC message and event page allocation when running in a standard, non-confidential VM environment. The core issue is that, under specific conditions, these pages are allocated but remain uninitialized, leading to a possible information leak.

I've traced the logic. The `is_confidential` flag checks for the availability of confidential VMBus capabilities, which are absent in standard Hyper-V setups. This results in `vmbus_is_confidential()` returning `false`. Consequently, the allocation routine marks the pages for decryption (`decrypt = true`), which should trigger memory zeroing via `set_memory_decrypted()`.

However, here's where it goes sideways. On a normal system *without* Confidential Computing (CC) enabled, `set_memory_decrypted()` effectively does nothing and immediately returns, because the `cc_platform_has(CC_ATTR_MEM_ENCRYPT)` check is false. The code *skips* the `memset` call that should clear the allocated memory.

The result? The `hyp_synic_message_page` and `hyp_synic_event_page` are allocated but contain garbage data from the kernel's memory pool.

Now, could KASAN flag this? No. KASAN is great, but it primarily focuses on memory access violations like out-of-bounds reads/writes and use-after-free conditions. Reading from a valid allocated page doesn't trigger KASAN, even if the page contains uninitialized data.

But KMSAN *would* catch this! It's designed to detect use of uninitialized memory. If the code subsequently uses the values in these uninitialized pages in conditional branches, KMSAN would immediately flag the access.

I then did a sweep of the entire patch series to see if this pattern was repeated, or if there were similar issues elsewhere:

*   I've checked other parts of the patch series.
    *   In a number of places, the code *correctly* checks for the presence of CC features before calling `set_memory_decrypted` or `memset`.
        *   Specifically, `arch/x86/hyperv/hv_init.c` correctly checks if `hv_isolation_type_snp()` is set.
        *   `arch/x86/kernel/kvmclock.c`, and `drivers/hv/connection.c` correctly checks `cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)`

    *   `arch/x86/hyperv/ivm.c`, `arch/arm64/mm/pageattr.c`, `arch/powerpc/platforms/pseries/svm.c`, `arch/s390/mm/init.c`, `arch/x86/coco/tdx/tdx.c` also show this pattern of conditional zeroing.
    *   In `drivers/dma-buf/heaps/system_heap.c` pages are allocated such that they are either zeroed by the page allocator or by `set_memory_decrypted()`.
    *   In  `arch/arm/mm/dma-mapping.c` the `attrs` variable is passed as 0, this does not have an impact.

*   However, the critical flaw resurfaces in `drivers/hv/hv.c`, where it is assumed that if the `decrypt` variable is true then memory zeroing should be enabled.  This `decrypt` variable is set without properly checking for the availability of CC features.

*   I went to check `kernel/dma/direct.c`, which makes calls such as `dma_set_decrypted`.

The overall conclusion is that it appears there are very few instances where this error is made. The main place to fix is in `drivers/hv/hv.c`.



137/3 2026/09/26 15:36 tool grepper
0m Args:
{
  "Expression": "force_dma_unencrypted"
}

Results:
{
  "Output": "Documentation/arch/x86/tdx.rst=489=For coherent DMA allocation, the DMA buffer gets converted on the\nDocumentation/arch/x86/tdx.rst:490:allocation. Check force_dma_unencrypted() for details.\nDocumentation/arch/x86/tdx.rst-491-\n--\narch/arm64/include/asm/mem_encrypt.h=20=int realm_register_memory_enc_ops(void);\narch/arm64/include/asm/mem_encrypt.h-21-\narch/arm64/include/asm/mem_encrypt.h:22:static inline bool force_dma_unencrypted(struct device *dev)\narch/arm64/include/asm/mem_encrypt.h-23-{\n--\narch/powerpc/include/asm/mem_encrypt.h=14=struct device;\narch/powerpc/include/asm/mem_encrypt.h-15-\narch/powerpc/include/asm/mem_encrypt.h:16:static inline bool force_dma_unencrypted(struct device *dev)\narch/powerpc/include/asm/mem_encrypt.h-17-{\n--\narch/s390/mm/init.c=129=int set_memory_decrypted(unsigned long vaddr, int numpages)\n--\narch/s390/mm/init.c-143-/* are we a protected virtualization guest? */\narch/s390/mm/init.c:144:bool force_dma_unencrypted(struct device *dev)\narch/s390/mm/init.c-145-{\n--\narch/x86/mm/mem_encrypt.c-19-/* Override for DMA direct allocation check - ARCH_HAS_FORCE_DMA_UNENCRYPTED */\narch/x86/mm/mem_encrypt.c:20:bool force_dma_unencrypted(struct device *dev)\narch/x86/mm/mem_encrypt.c-21-{\n--\ninclude/linux/dma-direct.h=114=static inline phys_addr_t dma_to_phys(struct device *dev, dma_addr_t dma_addr)\n--\ninclude/linux/dma-direct.h-128-#ifdef CONFIG_ARCH_HAS_FORCE_DMA_UNENCRYPTED\ninclude/linux/dma-direct.h:129:bool force_dma_unencrypted(struct device *dev);\ninclude/linux/dma-direct.h-130-#else\ninclude/linux/dma-direct.h:131:static inline bool force_dma_unencrypted(struct device *dev)\ninclude/linux/dma-direct.h-132-{\n--\ninclude/linux/dma-direct.h=137=static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size,\n--\ninclude/linux/dma-direct.h-148-\t */\ninclude/linux/dma-direct.h:149:\tif (!(attrs \u0026 DMA_ATTR_CC_SHARED) \u0026\u0026 force_dma_unencrypted(dev))\ninclude/linux/dma-direct.h-150-\t\treturn false;\n--\nkernel/dma/direct.c=42=u64 dma_direct_get_required_mask(struct device *dev)\nkernel/dma/direct.c-43-{\nkernel/dma/direct.c:44:\tbool require_decrypted = force_dma_unencrypted(dev);\nkernel/dma/direct.c-45-\tphys_addr_t phys = ((phys_addr_t)max_pfn \u003c\u003c PAGE_SHIFT) - 1;\n--\nkernel/dma/direct.c=73=bool dma_coherent_ok(struct device *dev, phys_addr_t phys, size_t size)\nkernel/dma/direct.c-74-{\nkernel/dma/direct.c:75:\tbool require_decrypted = force_dma_unencrypted(dev);\nkernel/dma/direct.c-76-\tdma_addr_t dma_addr = phys_to_dma_direct(dev, phys, require_decrypted);\n--\nkernel/dma/direct.c=202=void *dma_direct_alloc(struct device *dev, size_t size,\n--\nkernel/dma/direct.c-211-\nkernel/dma/direct.c:212:\tif (force_dma_unencrypted(dev))\nkernel/dma/direct.c-213-\t\tattrs |= __DMA_ATTR_ALLOC_CC_SHARED;\n--\nkernel/dma/direct.c=369=void dma_direct_free(struct device *dev, size_t size,\n--\nkernel/dma/direct.c-381-\t */\nkernel/dma/direct.c:382:\tif (force_dma_unencrypted(dev))\nkernel/dma/direct.c-383-\t\tattrs |= __DMA_ATTR_ALLOC_CC_SHARED;\n--\nkernel/dma/direct.c=447=struct page *dma_direct_alloc_pages(struct device *dev, size_t size,\n--\nkernel/dma/direct.c-455-\nkernel/dma/direct.c:456:\tif (force_dma_unencrypted(dev))\nkernel/dma/direct.c-457-\t\tattrs |= __DMA_ATTR_ALLOC_CC_SHARED;\n--\nkernel/dma/direct.c=500=void dma_direct_free_pages(struct device *dev, size_t size,\n--\nkernel/dma/direct.c-511-\t */\nkernel/dma/direct.c:512:\tbool mark_mem_encrypted = force_dma_unencrypted(dev);\nkernel/dma/direct.c-513-\n--\nkernel/dma/direct.c=676=int dma_direct_mmap(struct device *dev, struct vm_area_struct *vma,\n--\nkernel/dma/direct.c-686-\nkernel/dma/direct.c:687:\tif (force_dma_unencrypted(dev))\nkernel/dma/direct.c-688-\t\tattrs |= DMA_ATTR_CC_SHARED;\n--\nkernel/dma/direct.c=831=size_t dma_direct_max_mapping_size(struct device *dev)\n--\nkernel/dma/direct.c-835-\t    (dma_addressing_limited(dev) || is_swiotlb_force_bounce(dev) ||\nkernel/dma/direct.c:836:\t     force_dma_unencrypted(dev)))\nkernel/dma/direct.c-837-\t\treturn swiotlb_max_mapping_size(dev);\n--\nkernel/dma/mapping.c=633=void *dma_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle,\n--\nkernel/dma/mapping.c-654-\nkernel/dma/mapping.c:655:\tif (force_dma_unencrypted(dev))\nkernel/dma/mapping.c-656-\t\tattrs |= __DMA_ATTR_ALLOC_CC_SHARED;\n--\nkernel/dma/swiotlb.c=1554=phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr,\n--\nkernel/dma/swiotlb.c-1578-\t\t/* swiotlb pool is incorrect for this device */\nkernel/dma/swiotlb.c:1579:\t\tif (unlikely(mem-\u003ecc_shared != force_dma_unencrypted(dev)))\nkernel/dma/swiotlb.c-1580-\t\t\treturn (phys_addr_t)DMA_MAPPING_ERROR;\n"
}

138/3 2026/09/26 15:36 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 57307
							output: 487
							thoughts: 1801
						

Thoughts:
**Uninitialized Memory Vulnerability in Hyper-V Guest Driver**

Okay, so I've been digging into this memory initialization issue, and here's what I've found. Let's start with `include/linux/dma-direct.h` and the `force_dma_unencrypted` function. On x86, it essentially boils down to whether we're dealing with encrypted memory, and for a standard guest VM, it returns `false`, which is what I'd expect. Now, looking at `dma_direct_alloc` and `dma_direct_alloc_pages`, the key is that memory is *always* zeroed. It's either zeroed directly with `memset`, or, when `mark_mem_decrypt` is set, `dma_set_decrypted()` is called, which then uses `cc_make_shared()` to call `set_memory_decrypted()`, which *also* results in zeroing the memory if encryption is active. So, no surprises there.

The interesting bit is in `drivers/hv/hv.c`. In `hv_alloc_page`, if `decrypt` is true, `set_memory_decrypted` is called. Otherwise, `memset` is called. Now, look at `hv_synic_alloc`. When `vmbus_is_confidential()` is `false` (which is the *normal* case for Hyper-V), `decrypt` gets set to `true`. This means `hv_alloc_page` is called with `decrypt = true`. Inside `hv_alloc_page`, that calls `set_memory_decrypted`.

The thing is, in a standard Hyper-V VM (no SEV-SNP, no TDX, no memory encryption), `cc_platform_has(CC_ATTR_MEM_ENCRYPT)` is *false*. And looking at `arch/x86/mm/pat/set_memory.c`, the `__set_memory_enc_dec` function checks for this. When it's false, it skips the memory encryption part and just returns. Crucially, the code *doesn't* execute `memset`. This means that the memory allocated for `hyp_synic_message_page` and `hyp_synic_event_page` is *never* initialized. It contains completely garbage data!

This is where it gets really nasty. The `vmbus_on_msg_dpc` function in `drivers/hv/vmbus_drv.c` reads the message header directly from this uninitialized memory! Similarly, `vmbus_chan_sched` reads flags from the uninitialized event page! This leads to conditional branching and message dispatching being directly controlled by uninitialized memory!

Now, this is a textbook example of a flaw that's very difficult to detect. KASAN is useless here because it only tracks valid memory, and `__get_free_page` returns valid memory. KASAN can't detect when you read uninitialized data. However, KMSAN, which tracks byte-level initialization, WILL catch it. This is a genuine uninitialized memory issue that escapes traditional detection mechanisms. It's a critical bug!



139/3 2026/09/26 15:36 tool set-results
0m Args:
{
  "KMSANReasoning": "The patch series consolidates confidential computing (CC) shared/decrypted memory management and pushes zeroing into architecture-specific `set_memory_decrypted()` hooks. However, in `drivers/hv/hv.c:hv_alloc_page()`, the patch changes the unconditional `memset(*page, 0, PAGE_SIZE)` to only execute in the `else` branch of `if (decrypt)`:\n\n```c\n\tif (decrypt) {\n\t\tret = set_memory_decrypted((unsigned long)*page, 1);\n\t\tif (ret)\n\t\t\tgoto failed;\n\t} else {\n\t\tmemset(*page, 0, PAGE_SIZE);\n\t}\n```\n\nIn standard non-confidential Hyper-V guest VMs, `vmbus_is_confidential()` returns `false`, causing `hv_synic_alloc()` to set `decrypt = true`. On such systems, `cc_platform_has(CC_ATTR_MEM_ENCRYPT)` is false, meaning `set_memory_decrypted()` is a no-op that returns 0 without clearing the page. Consequently, `hv_alloc_page()` leaves `hyp_synic_message_page` and `hyp_synic_event_page` allocated via `__get_free_page(GFP_KERNEL)` completely uninitialized.\n\nWhen Hyper-V message DPC handling runs (`vmbus_drv.c:__vmbus_on_msg_dpc`), the kernel reads `msg = (struct hv_message *)message_page_addr + VMBUS_MESSAGE_SINT;` and branches on `msg_copy.header.message_type == HVMSG_NONE` and subsequent channel message headers using uninitialized memory.\n\nKASAN only tracks address bounds and object lifetime (use-after-free, out-of-bounds); because the allocated page is a valid memory object, KASAN cannot detect reads from uninitialized page contents or conditional logic branching on uninitialized data. Only KMSAN tracks page initialization shadow state and detects control flow / logic dependencies on uninitialized memory. Thus, a dedicated KMSAN fuzzing session is justified.",
  "NeedsKMSAN": true
}

Results:
{
  "KMSANReasoning": "The patch series consolidates confidential computing (CC) shared/decrypted memory management and pushes zeroing into architecture-specific `set_memory_decrypted()` hooks. However, in `drivers/hv/hv.c:hv_alloc_page()`, the patch changes the unconditional `memset(*page, 0, PAGE_SIZE)` to only execute in the `else` branch of `if (decrypt)`:\n\n```c\n\tif (decrypt) {\n\t\tret = set_memory_decrypted((unsigned long)*page, 1);\n\t\tif (ret)\n\t\t\tgoto failed;\n\t} else {\n\t\tmemset(*page, 0, PAGE_SIZE);\n\t}\n```\n\nIn standard non-confidential Hyper-V guest VMs, `vmbus_is_confidential()` returns `false`, causing `hv_synic_alloc()` to set `decrypt = true`. On such systems, `cc_platform_has(CC_ATTR_MEM_ENCRYPT)` is false, meaning `set_memory_decrypted()` is a no-op that returns 0 without clearing the page. Consequently, `hv_alloc_page()` leaves `hyp_synic_message_page` and `hyp_synic_event_page` allocated via `__get_free_page(GFP_KERNEL)` completely uninitialized.\n\nWhen Hyper-V message DPC handling runs (`vmbus_drv.c:__vmbus_on_msg_dpc`), the kernel reads `msg = (struct hv_message *)message_page_addr + VMBUS_MESSAGE_SINT;` and branches on `msg_copy.header.message_type == HVMSG_NONE` and subsequent channel message headers using uninitialized memory.\n\nKASAN only tracks address bounds and object lifetime (use-after-free, out-of-bounds); because the allocated page is a valid memory object, KASAN cannot detect reads from uninitialized page contents or conditional logic branching on uninitialized data. Only KMSAN tracks page initialization shadow state and detects control flow / logic dependencies on uninitialized memory. Thus, a dedicated KMSAN fuzzing session is justified.",
  "NeedsKMSAN": true
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)