From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id uQgsGPW2cmoxYwgAWB0awg (envelope-from ) for ; Wed, 05 Aug 2026 00:07:17 -0400 Authentication-Results: simark.ca; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20251104 header.b=UxTYBc61; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 553F61E166; Wed, 05 Aug 2026 00:07:17 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-5.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=unavailable autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [IPv6:2620:52:6:3111::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 6250C1E033 for ; Wed, 05 Aug 2026 00:07:15 -0400 (EDT) Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id A164D4BAE7E2 for ; Wed, 5 Aug 2026 04:07:13 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org A164D4BAE7E2 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20251104 header.b=UxTYBc61 Received: from mail-pf1-x431.google.com (mail-pf1-x431.google.com [IPv6:2607:f8b0:4864:20::431]) by sourceware.org (Postfix) with ESMTPS id 14FCC4BA799B for ; Wed, 5 Aug 2026 04:06:46 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 14FCC4BA799B Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 14FCC4BA799B Authentication-Results: sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::431 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1785902806; cv=none; b=WeEh0wZtSq6tDoV4pFEfdoIzDewc7oNhDknxJRVC6fwI9qpXzuoZMivnnc8ccvmKCNYli33naAhPyrSaz1GJ8EIw/+m9XCw6oksmZ1oRAD67g/h4RGH/eiB17ZOf3CkNbLTlhMvjUy0e06N5/Wn0mvvph5vzDwxPuPndSpPm9NU= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1785902806; c=relaxed/simple; bh=q5JuQfMnoVhgxs9ve1Ka0uwMr5AYsvXHM0qE3nAFUnY=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=jJmSio2cMtyj1eVphxfcuAaBSE6l0GgBHTDzWpUKUfo342jHn7vKhH0zDtTb6JBAPgtE27dlT5dofyBWvOKzhQfRsyPIOqA2DIZLuulABhTu7JM6Ph38UVpiDkqPCS39aLQbgF3tPlEPOODSclSuJSSU+EgxGrXPds9e9L/W+iw= ARC-Authentication-Results: i=1; sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20251104 header.b=UxTYBc61 DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 14FCC4BA799B Received: by mail-pf1-x431.google.com with SMTP id d2e1a72fcca58-84eb072307dso86337b3a.2 for ; Tue, 04 Aug 2026 21:06:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785902805; x=1786507605; darn=sourceware.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to:content-type; bh=yuWzmAG8WsKjQzobMqG9CZeyNz5WyM31bnhPcewcvBY=; b=UxTYBc61GOD2xxpKao2QxbHh4catFxdglonbXgCUUuRGIHfW2YKxnYlINTB7rPh1co P64pFQZcY7UcVwqY2AuiLXh8OSWL0/Q0I6gDm9MwE0ydTfJyu7NzVn4KVi+akqNt3GWe lRjykJXEGTwIL1H2qmHY8Rr1RYf1ZYql1tDn6wrXIzMOZLvQIsw2JrT608DwaMW762u9 xpXRgPU+TIh6OGKOg2jLkKlfEPhhHU9KD1egd/PTgpUowYPH9CyOWTDdoBjbyUtrV0QS SUO8Go8U3jT7RSYAR/Izww+tpvJ1sJXLZ/XExs/frVFl8ENaOa1ubsd3o9MMzYhzaUDx r6WA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785902805; x=1786507605; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=yuWzmAG8WsKjQzobMqG9CZeyNz5WyM31bnhPcewcvBY=; b=YrUW08Wt+qS74PwmdIiU2m+cw66srXknYhf7l32/vJAcIbmyI65GqPbeejnsUZOetT qhagrW2NPBrMnRehDYSKBg++OF7577AaYysTvr1Ri90E+wMmeQmNXHVAse/qeCU//lOT P+OKxreMglC0fm/udm413XCDJQL+QnZBVQY8CN1/MT6okRoRX/RBLxiaSx1WumD64Uke oQzC63RNNeWDx63efo7E81UqRR1oZA6TBaq27HYFGCIDXQ1z8ESx18s7TC91idqBZH/q intaY1up1BYCAEDPdSrJ9qqP+p8lsdTWc3ixBBs0z64k8MxIUahpOFZSpJRHPstjDud9 ZXag== X-Gm-Message-State: AOJu0YznewU1fCF8C0IMPTeJR0G2080xdk19dJKccTrYgXYsYrwSUwz5 5djy3wL/yzkV1qJ6Sdym6OzmZEHuUaFmTLssytK1z79GQX7xOvnx40M4BBlNZSlvYYM= X-Gm-Gg: AR+sD13D2s2YFIJh+8ERomBbYbneACDKS2tYNF8/yH447lf8kWduSVBgEhYiHSJqqW6 yjHk62p0VXXg2SsYPe/ftu5pFHYzlNatb5Q37XmGMEvJrH8vKCCfPlJwQdq8JKQsnR9HUFPOQlq YDsqbdhcCGJG+9cHI3q6LJ5XCnoD22JMWOysLFoLyvgMDTrmUi0n13wIvPqgWsbDP0ZpXpAYn1v K3rhDCun/R5aOs054vM0qTKwq0UUfD8ItG6j33MSS60XCt9XK2vXpaURcV84A7ZF+2z5e8V8vy3 RHj5E6YS5bJKmFMl5fgEHIxw88sXpMP/XwOwADnavUgyrDKdTlx14JBKisqm0YJCWS5IDf9f8N6 JAsgc6JBD8BvIX6fyVvEXpUIq4UX4MCEsUbEhpiDGmmGIDRMW+JEgZwY6eOudGxpoPqI0vyMQdp 3JYC9s3rE6Ocp69fg3IGpnfHlEkdexFapZZUdchCCbpijmY68UtQ8EBQjcqZ8PX7aH5+20dsMmI yiJpmFg7QzuTKFkFIrdtW3p7p/WfsigzqpRQMaAXcg= X-Received: by 2002:a17:90b:278e:b0:38e:76f8:fcbb with SMTP id 98e67ed59e1d1-3903c721be4mr2983748a91.4.1785902804818; Tue, 04 Aug 2026 21:06:44 -0700 (PDT) Received: from poweredge.r760.114.212.189.32.r760.114.212.189.32 ([155.254.126.227]) by smtp.gmail.com with ESMTPSA id a92af1059eb24-13fca52d329sm9069176c88.4.2026.08.04.21.06.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 04 Aug 2026 21:06:44 -0700 (PDT) From: Jielun Wu To: gdb-patches@sourceware.org Cc: Simon Marchi , Guinevere Larsen Subject: [PATCH v2] gdb: Check bounds before reading DWARF expression operands Date: Wed, 5 Aug 2026 12:06:40 +0800 Message-Id: <20260805040640.2095812-1-firmiana402@gmail.com> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 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 Some DWARF expression opcodes read fixed-width operands or block payloads before checking that the bytes are available in the current expression. Malformed expressions can consequently consume opcode or unrelated bytes and fail later with misleading results. Add helpers that validate byte ranges and fixed-width integer reads. Use them for the unchecked operands in dwarf_expr_context::execute_stack_op, including the size and payload of DW_OP_const_type. Validate entry-value blocks before constructing their nested expression view. Also guard dwarf_block_to_dwarf_reg_deref when its block ends after the base register offset, and add direct selftests for the two dwarf-block register parsers. Add a DWARF assembler test covering truncated operands and payloads. Tested on x86_64-linux. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=34239 Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=34299 Tested-By: Guinevere Larsen Signed-off-by: Jielun Wu --- Changes since v1: - Addressed review comments from Simon and Guinevere. - Clarified the byte helper comments and the validate-before-extract pattern. - Changed the fixed-width integer helpers to templated reference helpers. - Read fixed-width operands directly into the destination object where practical. - Added direct selftests for dwarf_block_to_dwarf_reg and dwarf_block_to_dwarf_reg_deref. - Used foreach_with_prefix in the DWARF assembler testcase. Guinevere tested v1 and reported no regressions. I kept her Tested-By trailer because v2 addresses review comments without any significant functional change. Tested on x86_64-linux: - make -C build -j32 - make -C build/gdb/testsuite check \ TESTS="gdb.dwarf2/dw2-bad-dwarf-expr-bounds.exp" - make -C build/gdb/testsuite check TESTS="gdb.gdb/unittest.exp" - make -C build/gdb/testsuite check \ TESTS="gdb.gdb/unittest.exp gdb.dwarf2/*.exp" gdb/dwarf2/expr.c | 224 +++++++++++++----- .../gdb.dwarf2/dw2-bad-dwarf-expr-bounds.exp | 174 ++++++++++++++ 2 files changed, 345 insertions(+), 53 deletions(-) create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-bad-dwarf-expr-bounds.exp diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c index 3a6b8f58199..de4cc8ffed6 100644 --- a/gdb/dwarf2/expr.c +++ b/gdb/dwarf2/expr.c @@ -34,6 +34,7 @@ #include "gdbarch.h" #include "objfiles.h" #include "extract-store-integer.h" +#include "gdbsupport/selftest.h" /* This holds gdbarch-specific types used by the DWARF expression evaluator. See comments in execute_stack_op. */ @@ -1370,6 +1371,57 @@ safe_skip_leb128 (const gdb_byte *buf, const gdb_byte *buf_end) error (_("DWARF expression error: ran off end of buffer reading leb128 value")); return buf; } + +/* Return BUF advanced by BYTES bytes. Throw an error if fewer than BYTES + bytes remain before BUF_END. */ + +static const gdb_byte * +safe_skip_bytes (const gdb_byte *buf, const gdb_byte *buf_end, + ULONGEST bytes) +{ + gdb_assert (buf <= buf_end); + if (bytes > (ULONGEST) (buf_end - buf)) + error (_("DWARF expression error: ran off end of buffer reading bytes")); + return buf + bytes; +} + +/* Read a LEN-byte unsigned integer at BUF, assign it to RESULT, and return + BUF advanced by LEN bytes. "Unsigned" describes how the bytes are decoded; + RESULT's type is chosen by the caller. Throw an error if fewer than LEN + bytes remain before BUF_END. */ + +template +static const gdb_byte * +safe_read_unsigned_integer (const gdb_byte *buf, const gdb_byte *buf_end, + int len, bfd_endian byte_order, T &result) +{ + gdb_assert (len >= 0); + gdb_assert ((size_t) len <= sizeof (T)); + + /* Validate the source range before extract_unsigned_integer reads it. */ + const gdb_byte *next = safe_skip_bytes (buf, buf_end, len); + result = static_cast (extract_unsigned_integer (buf, len, byte_order)); + return next; +} + +/* Read a LEN-byte signed integer at BUF, assign it to RESULT, and return BUF + advanced by LEN bytes. "Signed" describes how the bytes are decoded; + RESULT's type is chosen by the caller. Throw an error if fewer than LEN + bytes remain before BUF_END. */ + +template +static const gdb_byte * +safe_read_signed_integer (const gdb_byte *buf, const gdb_byte *buf_end, + int len, bfd_endian byte_order, T &result) +{ + gdb_assert (len >= 0); + gdb_assert ((size_t) len <= sizeof (T)); + + /* Validate the source range before extract_signed_integer reads it. */ + const gdb_byte *next = safe_skip_bytes (buf, buf_end, len); + result = static_cast (extract_signed_integer (buf, len, byte_order)); + return next; +} /* Check that the current operator is either at the end of an @@ -1488,6 +1540,9 @@ dwarf_block_to_dwarf_reg_deref (gdb::array_view block, return -1; if (offset != 0) return -1; + gdb_assert (buf <= buf_end); + if (buf == buf_end) + return -1; if (*buf == DW_OP_deref) { @@ -1688,9 +1743,9 @@ dwarf_expr_context::execute_stack_op (gdb::array_view expr) break; case DW_OP_addr: - result = extract_unsigned_integer (op_ptr, - this->m_addr_size, byte_order); - op_ptr += this->m_addr_size; + op_ptr = safe_read_unsigned_integer (op_ptr, op_end, + this->m_addr_size, + byte_order, result); /* Some versions of GCC emit DW_OP_addr before DW_OP_GNU_push_tls_address. In this case the value is an index, not an address. We don't support things like @@ -1723,44 +1778,44 @@ dwarf_expr_context::execute_stack_op (gdb::array_view expr) break; case DW_OP_const1u: - result = extract_unsigned_integer (op_ptr, 1, byte_order); + op_ptr = safe_read_unsigned_integer (op_ptr, op_end, 1, + byte_order, result); result_val = value_from_ulongest (address_type, result); - op_ptr += 1; break; case DW_OP_const1s: - result = extract_signed_integer (op_ptr, 1, byte_order); + op_ptr = safe_read_signed_integer (op_ptr, op_end, 1, + byte_order, result); result_val = value_from_ulongest (address_type, result); - op_ptr += 1; break; case DW_OP_const2u: - result = extract_unsigned_integer (op_ptr, 2, byte_order); + op_ptr = safe_read_unsigned_integer (op_ptr, op_end, 2, + byte_order, result); result_val = value_from_ulongest (address_type, result); - op_ptr += 2; break; case DW_OP_const2s: - result = extract_signed_integer (op_ptr, 2, byte_order); + op_ptr = safe_read_signed_integer (op_ptr, op_end, 2, + byte_order, result); result_val = value_from_ulongest (address_type, result); - op_ptr += 2; break; case DW_OP_const4u: - result = extract_unsigned_integer (op_ptr, 4, byte_order); + op_ptr = safe_read_unsigned_integer (op_ptr, op_end, 4, + byte_order, result); result_val = value_from_ulongest (address_type, result); - op_ptr += 4; break; case DW_OP_const4s: - result = extract_signed_integer (op_ptr, 4, byte_order); + op_ptr = safe_read_signed_integer (op_ptr, op_end, 4, + byte_order, result); result_val = value_from_ulongest (address_type, result); - op_ptr += 4; break; case DW_OP_const8u: - result = extract_unsigned_integer (op_ptr, 8, byte_order); + op_ptr = safe_read_unsigned_integer (op_ptr, op_end, 8, + byte_order, result); result_val = value_from_ulongest (address_type, result); - op_ptr += 8; break; case DW_OP_const8s: - result = extract_signed_integer (op_ptr, 8, byte_order); + op_ptr = safe_read_signed_integer (op_ptr, op_end, 8, + byte_order, result); result_val = value_from_ulongest (address_type, result); - op_ptr += 8; break; case DW_OP_constu: op_ptr = safe_read_uleb128 (op_ptr, op_end, &uoffset); @@ -1836,12 +1891,10 @@ dwarf_expr_context::execute_stack_op (gdb::array_view expr) uint64_t len; op_ptr = safe_read_uleb128 (op_ptr, op_end, &len); - if (op_ptr + len > op_end) - error (_("DW_OP_implicit_value: too few bytes available.")); this->m_len = len; this->m_data = op_ptr; this->m_location = DWARF_VALUE_LITERAL; - op_ptr += len; + op_ptr = safe_skip_bytes (op_ptr, op_end, len); dwarf_expr_require_composition (op_ptr, op_end, "DW_OP_implicit_value"); } @@ -1861,9 +1914,9 @@ dwarf_expr_context::execute_stack_op (gdb::array_view expr) int ref_addr_size = this->m_per_cu->ref_addr_size (); /* The referred-to DIE of sect_offset kind. */ - this->m_len = extract_unsigned_integer (op_ptr, ref_addr_size, - byte_order); - op_ptr += ref_addr_size; + op_ptr = safe_read_unsigned_integer (op_ptr, op_end, + ref_addr_size, byte_order, + this->m_len); /* The byte offset into the data. */ op_ptr = safe_read_sleb128 (op_ptr, op_end, &len); @@ -1972,7 +2025,8 @@ dwarf_expr_context::execute_stack_op (gdb::array_view expr) goto no_push; case DW_OP_pick: - offset = *op_ptr++; + op_ptr = safe_read_unsigned_integer (op_ptr, op_end, 1, + byte_order, offset); result_val = fetch (offset); in_stack_memory = fetch_in_stack_memory (offset); break; @@ -2016,7 +2070,16 @@ dwarf_expr_context::execute_stack_op (gdb::array_view expr) case DW_OP_deref_type: case DW_OP_GNU_deref_type: { - int addr_size = (op == DW_OP_deref ? this->m_addr_size : *op_ptr++); + int addr_size; + + if (op == DW_OP_deref) + addr_size = this->m_addr_size; + else + { + op_ptr = safe_read_unsigned_integer (op_ptr, op_end, 1, + byte_order, addr_size); + } + CORE_ADDR addr = fetch_address (0); struct type *type; @@ -2249,8 +2312,8 @@ dwarf_expr_context::execute_stack_op (gdb::array_view expr) break; case DW_OP_skip: - offset = extract_signed_integer (op_ptr, 2, byte_order); - op_ptr += 2; + op_ptr = safe_read_signed_integer (op_ptr, op_end, 2, byte_order, + offset); op_ptr += offset; goto no_push; @@ -2258,8 +2321,8 @@ dwarf_expr_context::execute_stack_op (gdb::array_view expr) { struct value *val; - offset = extract_signed_integer (op_ptr, 2, byte_order); - op_ptr += 2; + op_ptr = safe_read_signed_integer (op_ptr, op_end, 2, byte_order, + offset); val = fetch (0); dwarf_require_integral (val->type ()); if (value_as_long (val) != 0) @@ -2313,18 +2376,18 @@ dwarf_expr_context::execute_stack_op (gdb::array_view expr) case DW_OP_call2: { - cu_offset cu_off - = (cu_offset) extract_unsigned_integer (op_ptr, 2, byte_order); - op_ptr += 2; + cu_offset cu_off; + op_ptr = safe_read_unsigned_integer (op_ptr, op_end, 2, + byte_order, cu_off); this->dwarf_call (cu_off); } goto no_push; case DW_OP_call4: { - cu_offset cu_off - = (cu_offset) extract_unsigned_integer (op_ptr, 4, byte_order); - op_ptr += 4; + cu_offset cu_off; + op_ptr = safe_read_unsigned_integer (op_ptr, op_end, 4, + byte_order, cu_off); this->dwarf_call (cu_off); } goto no_push; @@ -2334,11 +2397,10 @@ dwarf_expr_context::execute_stack_op (gdb::array_view expr) ensure_have_per_cu (this->m_per_cu, "DW_OP_GNU_variable_value"); int ref_addr_size = this->m_per_cu->ref_addr_size (); - sect_offset sect_off - = (sect_offset) extract_unsigned_integer (op_ptr, - ref_addr_size, - byte_order); - op_ptr += ref_addr_size; + sect_offset sect_off; + op_ptr = safe_read_unsigned_integer (op_ptr, op_end, + ref_addr_size, byte_order, + sect_off); result_val = sect_variable_value (sect_off, this->m_per_cu, this->m_per_objfile); result_val = value_cast (address_type, result_val); @@ -2353,15 +2415,13 @@ dwarf_expr_context::execute_stack_op (gdb::array_view expr) union call_site_parameter_u kind_u; op_ptr = safe_read_uleb128 (op_ptr, op_end, &len); - if (op_ptr + len > op_end) - error (_("DW_OP_entry_value: too few bytes available.")); + const gdb_byte *expr_ptr = op_ptr; + op_ptr = safe_skip_bytes (op_ptr, op_end, len); - auto entry_value_expr = gdb::make_array_view (op_ptr, len); + auto entry_value_expr = gdb::make_array_view (expr_ptr, len); kind_u.dwarf_reg = dwarf_block_to_dwarf_reg (entry_value_expr); if (kind_u.dwarf_reg != -1) { - op_ptr += len; - if (trivial_entry_value (this->m_frame)) { /* We can assume that DW_OP_entry_value (expr) == expr. @@ -2384,7 +2444,6 @@ dwarf_expr_context::execute_stack_op (gdb::array_view expr) { if (deref_size == -1) deref_size = this->m_addr_size; - op_ptr += len; if (trivial_entry_value (this->m_frame)) { @@ -2410,9 +2469,9 @@ dwarf_expr_context::execute_stack_op (gdb::array_view expr) { union call_site_parameter_u kind_u; - kind_u.param_cu_off - = (cu_offset) extract_unsigned_integer (op_ptr, 4, byte_order); - op_ptr += 4; + op_ptr = safe_read_unsigned_integer (op_ptr, op_end, 4, + byte_order, + kind_u.param_cu_off); this->push_dwarf_reg_entry_value (CALL_SITE_PARAMETER_PARAM_OFFSET, kind_u, -1 /* deref_size */); @@ -2429,9 +2488,10 @@ dwarf_expr_context::execute_stack_op (gdb::array_view expr) op_ptr = safe_read_uleb128 (op_ptr, op_end, &uoffset); cu_offset type_die_cu_off = (cu_offset) uoffset; - n = *op_ptr++; + op_ptr = safe_read_unsigned_integer (op_ptr, op_end, 1, + byte_order, n); data = op_ptr; - op_ptr += n; + op_ptr = safe_skip_bytes (op_ptr, op_end, n); type = get_base_type (type_die_cu_off); @@ -2524,3 +2584,61 @@ dwarf_expr_context::execute_stack_op (gdb::array_view expr) this->m_recursion_depth--; gdb_assert (this->m_recursion_depth >= 0); } + +#if GDB_SELF_TEST +namespace selftests { + +static void +test_dwarf_block_to_dwarf_reg () +{ + const gdb_byte reg0[] = { DW_OP_reg0 }; + SELF_CHECK (dwarf_block_to_dwarf_reg (reg0) == 0); + + const gdb_byte regx[] = { DW_OP_regx, 32 }; + SELF_CHECK (dwarf_block_to_dwarf_reg (regx) == 32); + + const gdb_byte trailing_op[] = { DW_OP_reg0, DW_OP_stack_value }; + SELF_CHECK (dwarf_block_to_dwarf_reg (trailing_op) == -1); +} + +static void +test_dwarf_block_to_dwarf_reg_deref () +{ + CORE_ADDR deref_size; + + const gdb_byte deref[] = { DW_OP_breg0, 0, DW_OP_deref }; + SELF_CHECK (dwarf_block_to_dwarf_reg_deref (deref, &deref_size) == 0); + SELF_CHECK (deref_size == (CORE_ADDR) -1); + + const gdb_byte deref_size_4[] + = { DW_OP_bregx, 32, 0, DW_OP_deref_size, 4 }; + SELF_CHECK (dwarf_block_to_dwarf_reg_deref (deref_size_4, &deref_size) + == 32); + SELF_CHECK (deref_size == 4); + + const gdb_byte missing_deref[] = { DW_OP_breg0, 0 }; + SELF_CHECK (dwarf_block_to_dwarf_reg_deref (missing_deref, &deref_size) + == -1); + + const gdb_byte missing_size[] + = { DW_OP_breg0, 0, DW_OP_deref_size }; + SELF_CHECK (dwarf_block_to_dwarf_reg_deref (missing_size, &deref_size) + == -1); + + const gdb_byte nonzero_offset[] = { DW_OP_breg0, 1, DW_OP_deref }; + SELF_CHECK (dwarf_block_to_dwarf_reg_deref (nonzero_offset, &deref_size) + == -1); +} + +} /* namespace selftests. */ +#endif /* GDB_SELF_TEST. */ + +INIT_GDB_FILE (dwarf2expr) +{ +#if GDB_SELF_TEST + selftests::register_test ("dwarf_block_to_dwarf_reg", + selftests::test_dwarf_block_to_dwarf_reg); + selftests::register_test ("dwarf_block_to_dwarf_reg_deref", + selftests::test_dwarf_block_to_dwarf_reg_deref); +#endif /* GDB_SELF_TEST. */ +} diff --git a/gdb/testsuite/gdb.dwarf2/dw2-bad-dwarf-expr-bounds.exp b/gdb/testsuite/gdb.dwarf2/dw2-bad-dwarf-expr-bounds.exp new file mode 100644 index 00000000000..27e9033a2c3 --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dw2-bad-dwarf-expr-bounds.exp @@ -0,0 +1,174 @@ +# Copyright 2026 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Test malformed DWARF expressions whose opcodes have truncated operands. + +load_lib dwarf.exp + +require dwarf2_support + +standard_testfile main.c -dw.S + +set asm_file [standard_output_file $srcfile2] + +Dwarf::assemble $asm_file { + global srcfile + + declare_labels int_label + + cu {label cu_label version 5} { + DW_TAG_compile_unit { + DW_AT_name $srcfile + DW_AT_language @DW_LANG_C + } { + int_label: DW_TAG_base_type { + DW_AT_name "int" + DW_AT_encoding @DW_ATE_signed + DW_AT_byte_size 4 DW_FORM_sdata + } + + foreach {var op} { + truncated_addr DW_OP_addr + truncated_const1u DW_OP_const1u + truncated_const1s DW_OP_const1s + truncated_const2u DW_OP_const2u + truncated_const2s DW_OP_const2s + truncated_const4u DW_OP_const4u + truncated_const4s DW_OP_const4s + truncated_const8u DW_OP_const8u + truncated_const8s DW_OP_const8s + truncated_implicit_pointer DW_OP_implicit_pointer + truncated_gnu_implicit_pointer DW_OP_GNU_implicit_pointer + truncated_pick DW_OP_pick + truncated_deref_size DW_OP_deref_size + truncated_deref_type DW_OP_deref_type + truncated_gnu_deref_type DW_OP_GNU_deref_type + truncated_skip DW_OP_skip + truncated_bra DW_OP_bra + truncated_parameter_ref DW_OP_GNU_parameter_ref + } { + DW_TAG_variable { + DW_AT_name $var + DW_AT_type :$int_label + DW_AT_location { + variable _constants + + _op .byte $_constants($op) $op + } SPECIAL_expr + } + } + + DW_TAG_variable { + DW_AT_name "truncated_implicit_value" + DW_AT_type :$int_label + DW_AT_location { + variable _constants + + _op .byte $_constants(DW_OP_implicit_value) \ + DW_OP_implicit_value + _op .uleb128 4 "implicit value length" + _op .2byte 0 "truncated implicit value bytes" + } SPECIAL_expr + } + + foreach op {DW_OP_entry_value DW_OP_GNU_entry_value} { + set var "truncated_[string tolower $op]" + DW_TAG_variable { + DW_AT_name $var + DW_AT_type :$int_label + DW_AT_location { + variable _constants + + _op .byte $_constants($op) $op + _op .uleb128 4 "entry value expression length" + _op .2byte 0 "truncated entry value expression" + } SPECIAL_expr + } + } + + foreach op {DW_OP_const_type DW_OP_GNU_const_type} { + set op_name [string tolower $op] + + DW_TAG_variable { + DW_AT_name "truncated_${op_name}_size" + DW_AT_type :$int_label + DW_AT_location { + variable _constants + variable _cu_label + + _op .byte $_constants($op) $op + _op .uleb128 "$int_label - $_cu_label" \ + "type DIE offset" + } SPECIAL_expr + } + + DW_TAG_variable { + DW_AT_name "truncated_${op_name}_payload" + DW_AT_type :$int_label + DW_AT_location { + variable _constants + variable _cu_label + + _op .byte $_constants($op) $op + _op .uleb128 "$int_label - $_cu_label" \ + "type DIE offset" + _op .byte 4 "constant block length" + _op .2byte 0 "truncated constant block" + } SPECIAL_expr + } + } + } + } +} + +if {[prepare_for_testing "failed to prepare" ${testfile} \ + [list $srcfile $asm_file] nodebug]} { + return +} + +if {![runto_main]} { + return +} + +foreach_with_prefix var { + truncated_addr + truncated_const1u + truncated_const1s + truncated_const2u + truncated_const2s + truncated_const4u + truncated_const4s + truncated_const8u + truncated_const8s + truncated_implicit_value + truncated_implicit_pointer + truncated_gnu_implicit_pointer + truncated_pick + truncated_deref_size + truncated_deref_type + truncated_gnu_deref_type + truncated_skip + truncated_bra + truncated_dw_op_entry_value + truncated_dw_op_gnu_entry_value + truncated_parameter_ref + truncated_dw_op_const_type_size + truncated_dw_op_const_type_payload + truncated_dw_op_gnu_const_type_size + truncated_dw_op_gnu_const_type_payload +} { + gdb_test "print $var" \ + "DWARF expression error: ran off end of buffer reading bytes" +} -- 2.34.1