From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id AdtmHQnvBmrO7DwAWB0awg (envelope-from ) for ; Fri, 15 May 2026 06:01:45 -0400 Authentication-Results: simark.ca; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=XvapSsl+; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 71F1B1E0B1; Fri, 15 May 2026 06:01:45 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-3.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED,RCVD_IN_VALIDITY_CERTIFIED_BLOCKED, RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id CC4081E093 for ; Fri, 15 May 2026 06:01:43 -0400 (EDT) Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 6B31240A2485 for ; Fri, 15 May 2026 10:01:43 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 6B31240A2485 Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=XvapSsl+ Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTP id 424FD40A2487 for ; Fri, 15 May 2026 10:01:01 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 424FD40A2487 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 424FD40A2487 Authentication-Results: sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1778839261; cv=none; b=A496wQ0iyNx3tixOwlOAdqG/sLrfQna3hd3bnMSfTlJy75KiSfLohXd5Z+v3B+dII7yFaZJasRSY4aIir8uS5Mghtj5whu6EjAJ5zLS7SEtSmieB3jUz4a0aqflWwOey7C/l5DsGicb2u3xMjKFkbyfJfKjchkcIBCnd5LDi7hg= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1778839261; c=relaxed/simple; bh=6Yzs4CWOqk0pvv3C7B7u69u1U19O5WhgoDUkPWAefd4=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=UJ50taFGo8YAq5DR+F9g6nXPChVzWTpcKSevUjhdyMrvZHKIfZ1uZQ3N42rLXlp8uC8oOiSQZuRBFqY4SGdhy1+DJfNdqhM7zdPQX/bNMCXMCZ4FHFqd1iYoopBhITZtUlwFzSFERw8W/ZagXHxdqDNGCKWP6ziLC2FAWiGtaBg= ARC-Authentication-Results: i=1; sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=XvapSsl+ DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 424FD40A2487 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1778839261; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=1BE2GS5r4C/zFRhyAS26UXB67xLe2xjtbZ2ubu56DPg=; b=XvapSsl+0ePIND5aO8wHMWd2NOSlnueigbR4Kjn8m7sRG7lFlrCbigSqaZvAazuH+q9ARi F19AEUfccIFYjZijvRu989p0gNHyBzK9Zu6LG7cUHysRpUnYLXpftxCpSY11vfrRVdrCna afcR037hsS424DBzh3v1fYk4xdsaCfQ= Received: from mail-wm1-f72.google.com (mail-wm1-f72.google.com [209.85.128.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-567-2Ttb1HQ-O6K86uTnUMiY-w-1; Fri, 15 May 2026 06:00:59 -0400 X-MC-Unique: 2Ttb1HQ-O6K86uTnUMiY-w-1 X-Mimecast-MFC-AGG-ID: 2Ttb1HQ-O6K86uTnUMiY-w_1778839258 Received: by mail-wm1-f72.google.com with SMTP id 5b1f17b1804b1-48fd3dbd16aso22430215e9.0 for ; Fri, 15 May 2026 03:00:58 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778839257; x=1779444057; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=1BE2GS5r4C/zFRhyAS26UXB67xLe2xjtbZ2ubu56DPg=; b=oCEh3C0CmWR2Firp2zdEqQNdXpKWX2feENz44Fft5i1i/i3IZTx/GSJqX0Y8JdU5y+ ZByEe0tqWgAjx5vn8yr6I+ElxpdgbKLB60/pyrCe47Safr67gcNKYIbc2/S5VwJljXa8 Ap6TszwuCnW7k0mVsT1lZHspJ/bZhOOa2k5d3pBptsTwbpP+afLuR2fjvYHvhXQ09rE5 BxtbxkEfUXDso6LboIZCM8C9WmkCNt6GWLgNmhKRcLBtNkdlfNdnPtEBh+2ytl69dzfy U+W9N0PNhPBz2g0sj9Yte/CtttD70tO3mH302KF0oRK19XLZHQ9LSDSV0rVgc6vfMw9d y7bA== X-Gm-Message-State: AOJu0YysJV/pgk/EgYj1DuY5bCqAbnR1vZ0UHElFHL930gg0+nqUImY7 /0/EAcMOQh6skPq0fSIOsmv4YO4/rb8xnmcIWZfeOgrgd3jjJHe3+ZRzVJ2S90tf3D6Q1xOtVK5 QsI+d1W03zG/BKGNcISwqgmiXH231y9jckgX/tH+6RqiX05tMFcdGOCOZBJrZp1id+JkbzGpcs8 SGYEqYgdX98Vjy9x0MnP9FvPDk5irSWx4/5OALOc2hs4uMWmA= X-Gm-Gg: Acq92OGMdZtxi18OEsSibwhrGYV2r3KHzLbkNJf7H929Tuhkk46Ei3cOicvCFBIQU4z qMg9+463/oFkRnQRpY37nMVfOghTvjETCp1w2CDSdUwAi6Gvxhebzw44RuGv2l4N2bqWWErvGMk Pkql0/TQrGLjKUOHpi2thlbmgNps8qHL9Z5QvqO24RubCSQl/gk74di5cT7sMBGHTDk5Soucmp8 DTKSfkvEs5/RoQzfveZTV9AxdxWSuCbmjETSwiapO9mQ3JHYsUjkPO/Xvfux8z8QJWblPyNsO/y aAlGF7DrwMs3pUaVDxUleucXTuOui/9cLoWBHzwYOyuIh/0ND2p9blSMAxoq6mo6tVlQED8l6Sf /Dm68Ql4M7IrbRlRo9cVY5o0iE4A= X-Received: by 2002:a05:600c:34cc:b0:48e:60a3:220a with SMTP id 5b1f17b1804b1-48fe59af1cemr47412055e9.0.1778839257111; Fri, 15 May 2026 03:00:57 -0700 (PDT) X-Received: by 2002:a05:600c:34cc:b0:48e:60a3:220a with SMTP id 5b1f17b1804b1-48fe59af1cemr47410935e9.0.1778839256428; Fri, 15 May 2026 03:00:56 -0700 (PDT) Received: from localhost ([31.111.84.232]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-48fe5ab527asm57540825e9.11.2026.05.15.03.00.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 15 May 2026 03:00:55 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH 2/3] gdb/python: fix use of frame_info_ptr within pending_frame_object Date: Fri, 15 May 2026 11:00:47 +0100 Message-Id: X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: s_X1YoeZ3X8Q5htmILfvh3uznhj-x2wrEEpm_rpG95o_1778839258 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit content-type: text/plain; charset="US-ASCII"; x-default=true X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org The previous commit added a type trait which identifies types that should not be used within Python objects, that is, types that are not trivially default constructible. As a result of this, it was discovered that pending_frame_object includes a frame_info_ptr field. The problem with frame_info_ptr is that its constructor registers the new frame_info_ptr with the global frame_list. It is by this registration that invalidation of frame_info_ptr objects is performed. As Python is written in C, C++ constructors are not called, so when a pending_frame_object is created the constructor for the nested frame_info_ptr field is never run, and the frame_info_ptr is never registered with the global frame_list. As a result the frame_info_ptr will never be invalidated if the frame cache is flushed, this can then lead to problems where we make use of the 'frame_info *' within the frame_info_ptr, even though it is no longer valid. In this commit I change the frame_info_ptr within pending_frame_object to a 'frame_info_ptr *' and allocate the frame_info_ptr object on the heap, releasing the object, and resetting the point to NULL, when we are done with it. As the pending_frame_object only needs to remain valid for the duration of frame_unwind_python::sniff, the 'new' and 'delete' both performed within the function. We can now check that a pending_frame_object is valid by checking if the 'frame_info_ptr *' is NULL or not. As the frame_info_ptr is created in a valid state, and the point is set back to NULL when we are done with it, we no longer need to compare the frame_info_ptr object itself against NULL. The remaining changes in this patch are to dereference the 'frame_info_ptr *' in places where we need the actual object. In some cases I need to move the dereference later within a function, after a validity check, in order to avoid dereferencing a NULL pointer. Finally, I can add the static_assert that guarantees that pending_frame_object is now safe for allocation by Python. I discovered this bug while looking at PR gdb/32120. That bug is about a user's custom frame unwinder that triggers a flush of the frame cache during the sniffer phase (the RemoteTargetConnection.send_packet call switches thread, which triggers the frame cache flush). While looking at that bug I noticed that the frame_info_ptr within the pending_frame_object wasn't being reset when the frame cache was flushed. Fixing this does not resolve the user's issue, but I thought it was still worth tagging this commit with the bug link. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=32120 --- gdb/python/py-unwind.c | 53 ++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/gdb/python/py-unwind.c b/gdb/python/py-unwind.c index ed0ba89d267..fd305cb3316 100644 --- a/gdb/python/py-unwind.c +++ b/gdb/python/py-unwind.c @@ -68,13 +68,17 @@ show_pyuw_debug (struct ui_file *file, int from_tty, struct pending_frame_object : public PyObject { - /* Frame we are unwinding. */ - frame_info_ptr frame_info; + /* Frame we are unwinding. We cannot place a frame_info_ptr + directly within this struct as it is not trivially default + constructable. */ + frame_info_ptr *frame_info; /* Its architecture, passed by the sniffer caller. */ struct gdbarch *gdbarch; }; +static_assert (gdb::is_python_allocatable_v); + /* Saved registers array item. */ struct saved_reg @@ -245,9 +249,8 @@ unwind_infopy_repr (PyObject *self) unwind_info_object *unwind_info = (unwind_info_object *) self; pending_frame_object *pending_frame = (pending_frame_object *) (unwind_info->pending_frame); - frame_info_ptr frame = pending_frame->frame_info; - if (frame == nullptr) + if (pending_frame->frame_info == nullptr) return PyUnicode_FromFormat ("<%s for an invalid frame>", gdbpy_py_obj_tp_name (self)); @@ -263,6 +266,7 @@ unwind_infopy_repr (PyObject *self) saved_reg_names = (saved_reg_names + ", ") + name; } + frame_info_ptr frame (*pending_frame->frame_info); return PyUnicode_FromFormat ("<%s frame #%d, saved_regs=(%s)>", gdbpy_py_obj_tp_name (self), frame_relative_level (frame), @@ -331,7 +335,7 @@ unwind_infopy_add_saved_register (PyObject *self, PyObject *args, PyObject *kw) if (regnum >= gdbarch_num_cooked_regs (pending_frame->gdbarch)) { struct value *user_reg_value - = value_of_user_reg (regnum, pending_frame->frame_info); + = value_of_user_reg (regnum, *pending_frame->frame_info); if (user_reg_value->lval () == lval_register) regnum = user_reg_value->regnum (); if (regnum >= gdbarch_num_cooked_regs (pending_frame->gdbarch)) @@ -414,14 +418,15 @@ unwind_infopy_dealloc (PyObject *self) static PyObject * pending_framepy_str (PyObject *self) { - frame_info_ptr frame = ((pending_frame_object *) self)->frame_info; + pending_frame_object *pending_frame = (pending_frame_object *) self; const char *sp_str = NULL; const char *pc_str = NULL; - if (frame == NULL) + if (pending_frame->frame_info == nullptr) return PyUnicode_FromString ("Stale PendingFrame instance"); try { + frame_info_ptr frame (*pending_frame->frame_info); sp_str = core_addr_to_string_nz (get_frame_sp (frame)); pc_str = core_addr_to_string_nz (get_frame_pc (frame)); } @@ -439,14 +444,15 @@ static PyObject * pending_framepy_repr (PyObject *self) { pending_frame_object *pending_frame = (pending_frame_object *) self; - frame_info_ptr frame = pending_frame->frame_info; - if (frame == nullptr) + if (pending_frame->frame_info == nullptr) return gdb_py_invalid_object_repr (self); const char *sp_str = nullptr; const char *pc_str = nullptr; + frame_info_ptr frame (*pending_frame->frame_info); + try { sp_str = core_addr_to_string_nz (get_frame_sp (frame)); @@ -493,7 +499,7 @@ pending_framepy_read_register (PyObject *self, PyObject *args, PyObject *kw) get_frame_register_value() was used here, which did not handle the user register case. */ value *val = value_of_register - (regnum, get_next_frame_sentinel_okay (pending_frame->frame_info)); + (regnum, get_next_frame_sentinel_okay (*pending_frame->frame_info)); if (val == NULL) PyErr_Format (PyExc_ValueError, "Cannot read register %d from frame.", @@ -520,6 +526,10 @@ pending_framepy_is_valid (PyObject *self, PyObject *args) if (pending_frame->frame_info == nullptr) Py_RETURN_FALSE; + /* The frame_info field should never point at an uninitialized + object. */ + gdb_assert (*pending_frame->frame_info != nullptr); + Py_RETURN_TRUE; } @@ -538,7 +548,7 @@ pending_framepy_name (PyObject *self, PyObject *args) try { enum language lang; - frame_info_ptr frame = pending_frame->frame_info; + frame_info_ptr frame = *pending_frame->frame_info; name = find_frame_funname (frame, &lang, nullptr); } @@ -568,7 +578,7 @@ pending_framepy_pc (PyObject *self, PyObject *args) try { - pc = get_frame_pc (pending_frame->frame_info); + pc = get_frame_pc (*pending_frame->frame_info); } catch (const gdb_exception &except) { @@ -590,7 +600,7 @@ pending_framepy_language (PyObject *self, PyObject *args) try { - frame_info_ptr fi = pending_frame->frame_info; + frame_info_ptr fi = *pending_frame->frame_info; enum language lang = get_frame_language (fi); const language_defn *lang_def = language_def (lang); @@ -615,7 +625,7 @@ pending_framepy_find_sal (PyObject *self, PyObject *args) try { - frame_info_ptr frame = pending_frame->frame_info; + frame_info_ptr frame = *pending_frame->frame_info; symtab_and_line sal = find_frame_sal (frame); return symtab_and_line_to_sal_object (sal).release (); @@ -636,7 +646,7 @@ pending_framepy_block (PyObject *self, PyObject *args) PENDING_FRAMEPY_REQUIRE_VALID (pending_frame); - frame_info_ptr frame = pending_frame->frame_info; + frame_info_ptr frame = *pending_frame->frame_info; const struct block *block = nullptr, *fn_block; try @@ -682,7 +692,7 @@ pending_framepy_function (PyObject *self, PyObject *args) try { enum language funlang; - frame_info_ptr frame = pending_frame->frame_info; + frame_info_ptr frame = *pending_frame->frame_info; gdb::unique_xmalloc_ptr funname = find_frame_funname (frame, &funlang, &sym); @@ -774,7 +784,7 @@ pending_framepy_level (PyObject *self, PyObject *args) PENDING_FRAMEPY_REQUIRE_VALID (pending_frame); - int level = frame_relative_level (pending_frame->frame_info); + int level = frame_relative_level (*pending_frame->frame_info); return gdb_py_object_from_longest (level).release (); } @@ -863,9 +873,12 @@ frame_unwind_python::sniff (const frame_info_ptr &this_frame, return 0; } pfo->gdbarch = gdbarch; - pfo->frame_info = nullptr; - scoped_restore invalidate_frame = make_scoped_restore (&pfo->frame_info, - this_frame); + pfo->frame_info = new frame_info_ptr (this_frame); + SCOPE_EXIT + { + delete pfo->frame_info; + pfo->frame_info = nullptr; + }; /* Run unwinders. */ if (gdb_python_module == NULL -- 2.25.4