From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id H2qdAPPWOmr8OhUAWB0awg (envelope-from ) for ; Tue, 23 Jun 2026 14:56:51 -0400 Authentication-Results: simark.ca; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=RlEUVDDh; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id E567F1E098; Tue, 23 Jun 2026 14:56:50 -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.1 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIM_INVALID,DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED 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 6FC011E070 for ; Tue, 23 Jun 2026 14:56:49 -0400 (EDT) Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id E75DE4BA2E16 for ; Tue, 23 Jun 2026 18:56:48 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org E75DE4BA2E16 Authentication-Results: sourceware.org; dkim=fail reason="signature verification failed" (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=RlEUVDDh 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 AFAF34BA2E12 for ; Tue, 23 Jun 2026 18:56:13 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org AFAF34BA2E12 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 AFAF34BA2E12 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=1782240973; cv=none; b=Xjc9OOYGzGV5kY4hw7/afiUCLkBaxwXvfSSQuwh2ZCfuDi2q9jtZB1Ka8lL1vYQ6EHQsot17v1Pz25y3w1gPAEfUtfgMbJGJZHHd/X2jjDmmP+q9EbNkRqhteMswwYG520cQ7x69ID+Lgq8ap036E6YbLgw3UCTRQxm13hh8v08= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1782240973; c=relaxed/simple; bh=QnDzYKgsS52aQu6LDaWVE4x8ptB8ZJqnzK83YEU0+GU=; h=DKIM-Signature:Message-ID:Date:MIME-Version:Subject:To:From; b=oJTHQmcBVqbZDTemll8iDFRqy9aEm2w2809KAkWHKZN5nyApcDzcNn+8TzLU+jtOSY+ZEzaQsLa+mjHV7I8Ym8X2Kz4ubFh99youUddO+Pe4tKEmICEZ1ExPfuqtz7lw59iAll8W9W4Ga4jI240OEloLtuK2la2KygcBPLg+j6k= ARC-Authentication-Results: i=1; sourceware.org; dkim=fail (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=RlEUVDDh reason="signature verification failed" DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org AFAF34BA2E12 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1782240973; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=aCLfgksRDbU5DlDZKYM1cJgyuSZk/h1mv0Ix/D/mtmU=; b=RlEUVDDhCxTEpSJdSMvLzgP6V0aGktks3wRgXs0LHf+HemaeXKuXdyF1IaRPypOKPo5TW8 +GCUGQ47DriEvrhNcGt+5KlubNBi9BMlecKxoTQyTXAIKN2nPngGtFibhK8hLY6zo8Hjy5 s5b6UjJBTQ45MRceUPES1dFv6+o0p0k= Received: from mail-ua1-f71.google.com (mail-ua1-f71.google.com [209.85.222.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-653-H_BlIhJ_OlG5CNnYG0oJhw-1; Tue, 23 Jun 2026 14:56:10 -0400 X-MC-Unique: H_BlIhJ_OlG5CNnYG0oJhw-1 X-Mimecast-MFC-AGG-ID: H_BlIhJ_OlG5CNnYG0oJhw_1782240969 Received: by mail-ua1-f71.google.com with SMTP id a1e0cc1a2514c-963b07e2003so164880241.0 for ; Tue, 23 Jun 2026 11:56:09 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1782240969; x=1782845769; h=content-transfer-encoding:in-reply-to:from:content-language :references:to:subject:user-agent:mime-version:date:message-id :x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=YlD07HnfHMf+jCMsycAdxHeq6I9Sr1LqtmCjbkvPZCM=; b=LZSS9Iyg/Mzp/3NvxyJadpAt8UEALKwoEVN/y54PdG1f1/Z1v1xn0kby9Srsq/nwa+ DRSNyjZnpeAEcXJHayFwX3WceG3XP84Wgb/qQsxknFP7Wq29hbYIErsOSDAnBxwb6KuU M8EDUc+4qqwmfi7LbJddeeN5/thB74u2xZQHNkqB2tNPJnBBEnNjgDNlnZ+UxKQtwC3a rX/7G3OEYOtOhCFwl6LRWKdur5XuJTY3LbOGMsBe6ohG9NWzy4IOVF5qqRJxp4Bzkyza WI+DMi+OH/J0l27rbg+aMTlDi7b5i/KSardkXNI9fB/wGwKLF4EnNpu6ujrOK8wAfH3d kZWQ== X-Forwarded-Encrypted: i=1; AFNElJ/9COEhNE8YCXhL4c7KSy51HgWjgXPUcoQL9g/G++S7SqvodkxwjnP+xTsDAHLKrmVX0OZ8Hfi3l4OheQ==@sourceware.org X-Gm-Message-State: AOJu0YwuRjDoLp+qzYxtriaChxQ5kiFAVmeVnQngk+7FVct/Ks39rji9 lo7E+OeTwrFEJ2LrmMqDpeu4A0A1ujlegyZgh/jqp7e82Oe1679iKgDIaujUVyORD52I6WiiOmw R+/EdmMSgymGv7YrJHq4a0TyHXOj00DvZPmMo/aIO349HJWHi2pPUeR507AoU3u44C8sqpzY= X-Gm-Gg: AfdE7cmKbI06aFMfL9DrE9eYVgXaCudV2i2b1CYsFCMYTmVYHKE4o+HBz4hBp0bSJy3 0DbUZMEj8CCR6jjBeTIuoSAGyZoUiGfPGJJ8wPNDlgzZH8sifRndCDmMFOaM2RdTrpBgEFqq3eM sQsS940yxchZ3z7x4z5EJ/SVQX0sL2nN6XIH+nAaQT0zT0qQeZOyM6wYaD2TmEA2NQRPErhe1Dr onllT7IRaEBnf7+bAOcgMBy0MBo0yBVqhLQkbFgmd9UnOcc/k1d3OchWZTHAA9I90rPR9DEbeVj 3lH9ZQUdn3D89mKRD7x9yvVj79eShWK+m894VLm0TypvScqh7w3PISTYDG940T8ZpqlwZDNlblM YftPWKxFmnor5n0YN9Jwg X-Received: by 2002:a05:6102:358f:b0:71e:1c56:e794 with SMTP id ada2fe7eead31-72ff3f837bbmr2584446137.7.1782240969229; Tue, 23 Jun 2026 11:56:09 -0700 (PDT) X-Received: by 2002:a05:6102:358f:b0:71e:1c56:e794 with SMTP id ada2fe7eead31-72ff3f837bbmr2584427137.7.1782240968530; Tue, 23 Jun 2026 11:56:08 -0700 (PDT) Received: from ?IPV6:2804:14d:8084:993e::75d? ([2804:14d:8084:993e::75d]) by smtp.gmail.com with ESMTPSA id a1e0cc1a2514c-9670c2c0c23sm8849130241.8.2026.06.23.11.56.07 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Tue, 23 Jun 2026 11:56:08 -0700 (PDT) Message-ID: Date: Tue, 23 Jun 2026 15:56:04 -0300 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH][PR gdb/34239][PR gdb/34299] gdb: Check bounds before reading DWARF expression operands To: Jielun Wu , gdb-patches@sourceware.org References: <20260618180158.2893540-1-firmiana402@gmail.com> From: Guinevere Larsen In-Reply-To: <20260618180158.2893540-1-firmiana402@gmail.com> X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: 6ZnxOR8s80kAvpYO-vbQvBVsvixkA4A_eenk0Mbxkig_1782240969 X-Mimecast-Originator: redhat.com Content-Language: en-US Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit 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 On 6/18/26 3:01 PM, Jielun Wu wrote: > Some DWARF expression opcodes read fixed-width operands or block payloads > from the expression buffer before checking that the bytes are available. > With a malformed expression, this can read past the end of the expression > buffer. > > Add helpers that validate fixed-width integer reads and block payload > skips, and use them in dwarf_expr_context::execute_stack_op. Also guard > the nested DW_OP_entry_value parser before reading the following opcode. > > Add a DWARF assembler test that exercises truncated operands for these > opcodes. > > 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 > --- Hi! Thanks for working on this! I'm not too familiar with the dwarf parser, so I can only do a superficial review. Most of my comments are questions and nitpicks, I don't think you necessarily need to send a new version just from this review. I have tested this and see no regressions, so feel free to add my test tag to the git trailers Tested-By: Guinevere Larsen > gdb/dwarf2/expr.c | 166 +++++++++------ > .../gdb.dwarf2/dw2-bad-dwarf-expr-bounds.exp | 190 ++++++++++++++++++ > 2 files changed, 300 insertions(+), 56 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 c71d4725b03..ead918388a4 100644 > --- a/gdb/dwarf2/expr.c > +++ b/gdb/dwarf2/expr.c > @@ -1370,7 +1370,45 @@ 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; > } > - > + > +/* Helper to skip BYTES bytes or throw an error. */ > + > +static const gdb_byte * > +safe_skip_bytes (const gdb_byte *buf, const gdb_byte *buf_end, > + ULONGEST bytes) > +{ > + if (buf > buf_end || bytes > (ULONGEST) (buf_end - buf)) > + error (_("DWARF expression error: ran off end of buffer reading bytes")); > + return buf + bytes; > +} > + > +/* Helper to read a fixed-width unsigned integer or throw an error. */ > + > +static const gdb_byte * > +safe_read_unsigned_integer (const gdb_byte *buf, const gdb_byte *buf_end, > + int len, bfd_endian byte_order, uint64_t *r) > +{ > + gdb_assert (len >= 0); > + > + const gdb_byte *data = buf; > + buf = safe_skip_bytes (buf, buf_end, len); > + *r = extract_unsigned_integer (data, len, byte_order); > + return buf; Oh, ok, it took me a while of thinking but I think I now understand why you wrote it like this. You advance the buffer first to use that as a check that it is possible to read that many bytes from the buffer, instead of needing to double up on the if condition, right? If so, I think it would be nice to have a comment explaining it, because I went through 2 incorrect explanations before reaching that conclusion. > +} > + > +/* Helper to read a fixed-width signed integer or throw an error. */ > + > +static const gdb_byte * > +safe_read_signed_integer (const gdb_byte *buf, const gdb_byte *buf_end, > + int len, bfd_endian byte_order, int64_t *r) > +{ > + gdb_assert (len >= 0); > + > + const gdb_byte *data = buf; > + buf = safe_skip_bytes (buf, buf_end, len); > + *r = extract_signed_integer (data, len, byte_order); > + return buf; Similar comment as above > +} > > /* Check that the current operator is either at the end of an > expression, or that it is followed by a composition operator or by > @@ -1478,7 +1516,7 @@ dwarf_block_to_dwarf_reg_deref (gdb::array_view block, > if (buf == NULL) > return -1; > if ((int) dwarf_reg != dwarf_reg) > - return -1; > + return -1; > } > else > return -1; > @@ -1488,6 +1526,8 @@ dwarf_block_to_dwarf_reg_deref (gdb::array_view block, > return -1; > if (offset != 0) > return -1; > + if (buf >= buf_end) > + return -1; > > if (*buf == DW_OP_deref) > { > @@ -1498,7 +1538,7 @@ dwarf_block_to_dwarf_reg_deref (gdb::array_view block, > { > buf++; > if (buf >= buf_end) > - return -1; > + return -1; > *deref_size_return = *buf++; > } > else > @@ -1688,9 +1728,10 @@ 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, &uoffset); Why not just send "&result" as the parameter here, instead of &uoffset Same question for all the occurrences below > + result = uoffset; > /* 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 +1764,52 @@ 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, &uoffset); > + result = uoffset; > 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, &offset); > + result = offset; > 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, &uoffset); > + result = uoffset; > 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, &offset); > + result = offset; > 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, &uoffset); > + result = uoffset; > 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, &offset); > + result = offset; > 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, &uoffset); > + result = uoffset; > 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, &offset); > + result = offset; > 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 +1885,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 +1908,10 @@ 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, > + &uoffset); > + this->m_len = uoffset; Again, why not send "&this->m_len" ? > > /* The byte offset into the data. */ > op_ptr = safe_read_sleb128 (op_ptr, op_end, &len); > @@ -1972,7 +2020,9 @@ 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, &uoffset); > + offset = uoffset; > result_val = fetch (offset); > in_stack_memory = fetch_in_stack_memory (offset); > break; > @@ -2016,7 +2066,13 @@ 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 = this->m_addr_size; > + if (op != DW_OP_deref) > + { > + op_ptr = safe_read_unsigned_integer (op_ptr, op_end, 1, > + byte_order, &uoffset); > + addr_size = uoffset; Maybe I'm missing something, but this seems like an incorrect change? It seems like you should call safe_skip_bytes instead of read_unsigned_integer, and assign addr_size to *op_ptr? > + } > CORE_ADDR addr = fetch_address (0); > struct type *type; > > @@ -2249,8 +2305,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; This should also use safe_skip_bytes > goto no_push; > > @@ -2258,8 +2314,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 +2369,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; > + op_ptr = safe_read_unsigned_integer (op_ptr, op_end, 2, > + byte_order, &uoffset); > + cu_offset cu_off = (cu_offset) uoffset; > 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; > + op_ptr = safe_read_unsigned_integer (op_ptr, op_end, 4, > + byte_order, &uoffset); > + cu_offset cu_off = (cu_offset) uoffset; > this->dwarf_call (cu_off); > } > goto no_push; > @@ -2334,11 +2390,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; > + op_ptr = safe_read_unsigned_integer (op_ptr, op_end, > + ref_addr_size, byte_order, > + &uoffset); > + sect_offset sect_off = (sect_offset) uoffset; > 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 +2408,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 +2437,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 +2462,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, &uoffset); > + kind_u.param_cu_off = (cu_offset) uoffset; > this->push_dwarf_reg_entry_value (CALL_SITE_PARAMETER_PARAM_OFFSET, > kind_u, > -1 /* deref_size */); > @@ -2429,9 +2481,11 @@ 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, > + &uoffset); > + n = uoffset; > data = op_ptr; > - op_ptr += n; > + op_ptr = safe_skip_bytes (op_ptr, op_end, n); > > type = get_base_type (type_die_cu_off); > > 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..7f5fa22388d > --- /dev/null > +++ b/gdb/testsuite/gdb.dwarf2/dw2-bad-dwarf-expr-bounds.exp > @@ -0,0 +1,190 @@ > +# 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} { > + 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 { > + _op .byte $Dwarf::_constants($op) $op > + } SPECIAL_expr > + } > + } > + > + DW_TAG_variable { > + DW_AT_name "truncated_implicit_value" > + DW_AT_type :$int_label > + DW_AT_location { > + _op .byte $Dwarf::_constants(DW_OP_implicit_value) \ > + DW_OP_implicit_value > + _op .uleb128 4 "implicit value length" > + _op .2byte 0 "truncated implicit value bytes" > + } SPECIAL_expr > + } > + > + DW_TAG_variable { > + DW_AT_name "truncated_entry_value" > + DW_AT_type :$int_label > + DW_AT_location { > + _op .byte $Dwarf::_constants(DW_OP_entry_value) \ > + DW_OP_entry_value > + _op .uleb128 4 "entry value expression length" > + _op .2byte 0 "truncated entry value expression" > + } SPECIAL_expr > + } > + > + DW_TAG_variable { > + DW_AT_name "truncated_gnu_entry_value" > + DW_AT_type :$int_label > + DW_AT_location { > + _op .byte $Dwarf::_constants(DW_OP_GNU_entry_value) \ > + DW_OP_GNU_entry_value > + _op .uleb128 4 "entry value expression length" > + _op .2byte 0 "truncated entry value expression" > + } SPECIAL_expr > + } > + > + DW_TAG_variable { > + DW_AT_name "truncated_const_type_size" > + DW_AT_type :$int_label > + DW_AT_location { > + _op .byte $Dwarf::_constants(DW_OP_const_type) \ > + DW_OP_const_type > + _op .uleb128 "$int_label - $cu_label" "type DIE offset" > + } SPECIAL_expr > + } > + > + DW_TAG_variable { > + DW_AT_name "truncated_const_type_payload" > + DW_AT_type :$int_label > + DW_AT_location { > + _op .byte $Dwarf::_constants(DW_OP_const_type) \ > + DW_OP_const_type > + _op .uleb128 "$int_label - $cu_label" "type DIE offset" > + _op .byte 4 "constant block length" > + _op .2byte 0 "truncated constant block" > + } SPECIAL_expr > + } > + > + DW_TAG_variable { > + DW_AT_name "truncated_gnu_const_type_size" > + DW_AT_type :$int_label > + DW_AT_location { > + _op .byte $Dwarf::_constants(DW_OP_GNU_const_type) \ > + DW_OP_GNU_const_type > + _op .uleb128 "$int_label - $cu_label" "type DIE offset" > + } SPECIAL_expr > + } > + > + DW_TAG_variable { > + DW_AT_name "truncated_gnu_const_type_payload" > + DW_AT_type :$int_label > + DW_AT_location { > + _op .byte $Dwarf::_constants(DW_OP_GNU_const_type) \ > + DW_OP_GNU_const_type > + _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 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_entry_value > + truncated_gnu_entry_value > + truncated_parameter_ref > + truncated_const_type_size > + truncated_const_type_payload > + truncated_gnu_const_type_size > + truncated_gnu_const_type_payload > +} { > + with_test_prefix $var { There exists a "foreach_with_prefix", that does basically what you did here. > + gdb_test "print $var" \ > + ".*DWARF expression error: ran off end of buffer reading .*" > + } > +} -- Cheers, Guinevere Larsen it/its she/her (deprecated)