From b1c06fdb09c45e018804ef603058abf866a910fc Mon Sep 17 00:00:00 2001 From: Nick Brereton <85175726+nbstrike@users.noreply.github.com> Date: Fri, 4 Sep 2026 14:27:11 -0400 Subject: [PATCH] Fix use-after-free of captured interface typedef in deleted generate branch (#8287) --- src/V3Param.cpp | 1 + test_regress/t/t_iface_typedef_capture_uaf.v | 25 ++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 0d8a01239..3515d25cd 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -3269,6 +3269,7 @@ class ParamVisitor final : public VNVisitor { } else { nodep->unlinkFrBack(); } + V3LinkDotIfaceCapture::purgeDeletedSubtree(nodep); VL_DO_DANGLING(nodep->deleteTree(), nodep); // Normal edit rules will now recurse the replacement } else { diff --git a/test_regress/t/t_iface_typedef_capture_uaf.v b/test_regress/t/t_iface_typedef_capture_uaf.v index a9b6d4008..97c4b4dde 100644 --- a/test_regress/t/t_iface_typedef_capture_uaf.v +++ b/test_regress/t/t_iface_typedef_capture_uaf.v @@ -38,6 +38,22 @@ module avmm_autopipeline ( always_comb slave.req = internal_req; endmodule +module genif_body #( + parameter bit ENABLE = 1 +) ( + avmm_if.slave master, + avmm_if.master slave +); + if (ENABLE) begin : gen_on + typedef master.request_t request_t; + request_t internal_req; + assign internal_req = master.req; + always_comb slave.req = internal_req; + end else begin : gen_off + always_comb slave.req = '0; + end +endmodule + module t; for (genvar g = 0; g < 2; ++g) begin : gen_block avmm_if #(.DW(32 * (g + 1))) m_if (); @@ -48,8 +64,17 @@ module t; ); end + avmm_if #(.DW(32)) on_m_if (); + avmm_if #(.DW(32)) on_s_if (); + avmm_if #(.DW(64)) off_m_if (); + avmm_if #(.DW(64)) off_s_if (); + // ENABLE=0 deletes the generate branch holding the captured typedef + genif_body #(.ENABLE(1)) body_on (.master(on_m_if), .slave(on_s_if)); + genif_body #(.ENABLE(0)) body_off (.master(off_m_if), .slave(off_s_if)); + initial begin #1; + `checkd($bits(body_on.gen_on.internal_req), 33); `checkd($bits(gen_block[0].pipe.internal_req), 33); `checkd($bits(gen_block[1].pipe.internal_req), 65); $write("*-* All Finished *-*\n");