From patchwork Sun Mar 29 02:42:25 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alan Modra X-Patchwork-Id: 132414 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id F36C04B9DB43 for ; Sun, 29 Mar 2026 02:43:24 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org F36C04B9DB43 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=FMiXQfq7 X-Original-To: binutils@sourceware.org Delivered-To: binutils@sourceware.org Received: from mail-pg1-x536.google.com (mail-pg1-x536.google.com [IPv6:2607:f8b0:4864:20::536]) by sourceware.org (Postfix) with ESMTPS id DF27B4BA9036 for ; Sun, 29 Mar 2026 02:42:30 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org DF27B4BA9036 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 DF27B4BA9036 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::536 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1774752151; cv=none; b=PGoHR/notue1Clrh4V1T7gXxPR6q3ix/ipHRoKe3216paPWTW+SvtiV9M5vdj3fqiwICAP0aAcw7i2MOVFr3DwMAyg2mnBSIMqcfbECEE+21/lsI26xNHi8goU794GHU2XnWvCUR1J+Gzg/JPCJIo3+cmComgrpueZHhVVrTKuM= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1774752151; c=relaxed/simple; bh=66iaRuKnIkbaqeMpIMDOFBke3K56F9oOCjnWo1pzqsw=; h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version; b=mL2gE1bJpCjyJ9dl5XgFWA/he5kn80PkGmYaAYNBzJqBn48onaXMlwyT6FXCfN3020a4rphGS2IGYcwJxR7C4ZkDNHq7QBnOYRLwYXe9hHPZEwblC21P6r70ZGMjt+3a2LeORX59C2Zkvsstg67Lc7tf7Eb6iiHuVTK5/Gy71Ag= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org DF27B4BA9036 Received: by mail-pg1-x536.google.com with SMTP id 41be03b00d2f7-c76682834a3so1317690a12.3 for ; Sat, 28 Mar 2026 19:42:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1774752150; x=1775356950; darn=sourceware.org; h=content-disposition:mime-version:message-id:subject:to:from:date :from:to:cc:subject:date:message-id:reply-to; bh=xBgzcEDZFKH9k8qWvey1gTfcCcBXZg7X+orsmcRE4dY=; b=FMiXQfq7PB4ZlHqoLJSNqOeJsbVAiShc8/cV3zdpghinRAqu67JZ8K0Wn5qIFk/DNo G00HbtEBZlgXqFMdkUROAsfI56RdUDNBqQRSzK6loSazZMi/SH98lGdjPjGaVrEIP1Vo KBc+aWmxAnEu78CnI6VqU1y8Aviwlta6/Eb+xo1sEj+IsmJ9HjSAUxcEq+NnGwx8VUlr s8XqnYjT4/ZWhn+kXgjFEvMOMgj1rorJEhnCL+i+xXxz+7pG25q9yRy1U95dJyT1IPFT b0UO5F2r68whZoUawnGuIP/j06fwZHudLJIlLDvZCBvX9eNO4/5OI7x8Aqg4zLF0CcvU NINw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774752150; x=1775356950; h=content-disposition:mime-version:message-id:subject:to:from:date :x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=xBgzcEDZFKH9k8qWvey1gTfcCcBXZg7X+orsmcRE4dY=; b=YQlEJ6XyPfMirRGJaxnxIBwXJdRlDXHgSZGyiMm2SoCH7sZcMbZQRbUWZDaEYRlSg9 /8BCY0HF9je9jExdzd3jW6w5TmHb9302DrkDhTeLBPTT7MYvkzTmXe9K2pVCuxlFhDOB VxKTGBWQ/4SxsFleFnCofym/MXwJSz+IoUpdyDXMF1ML93H37K0BghUyz6F35piwgfSf xqN7E81pnugJYb7fBjL+rLSLBPyPbOpxkzMqGPc+rZKzr8/WWTf226hPyhuBMC8+NCi2 lAfiUB5Sltf5ftyYpYHjV/z03T7aeJjqM1K8ImxLAQ3IH08OMfy+sJvk9vTACLZmfKJQ UI8Q== X-Gm-Message-State: AOJu0YxTUIrboHTNZ0rsp2E5Te3BBJP4yue1nVNkSjvkaPZF2JUOJlQX MMxOjMrvtCSJ13/93AzjoH8mmIPCws+QKgr3eQbnI65YiJvYYMf8sXVspZnXLA== X-Gm-Gg: ATEYQzwH6/KgDnHcXtF6E49HG1oMFsgh0/zy7mJ1nE7+O53UovJAZiKnXdzUpzVgU+n GB8etWeFtDkscx1L62m/BZrvpFBIHJE6Vd/wPR/eWN5DD8U6kbjbOowD6DbYGvmW2w9qTtp42Sp wUq2AINeF8k17t8a0b30IM0VrnT14WHJgXTa3BIx/jzmOQd54XysTfJCOwHdohBOwRhHZuDrupb Xnm/BQOnMYuRG6itvL+JVcHBr+SZIv4mjBFfhjfQVMckvItbkkys8H3PV8gm1MvPSrOhjmG7nZs YAsV2vvnqjhnk7H93qei8Oo9cMiIjMCb6Rn8et/fXy0pLYM9BsiLPewcodNorgrIVwIdlU3J+YX nXmr1cFgSi45p9OSJ/TM8xNwv+FyrOEzV6H8CHjwtS72VOZc5+LSBJR21XF+N6WMoCPxtyWwIJi PZKxzZmFQznC46eZTHuU0XVLWA3DE/ynmEOALO X-Received: by 2002:a05:6a20:918b:b0:398:c351:aa0e with SMTP id adf61e73a8af0-39c878a2bcemr8782402637.25.1774752149400; Sat, 28 Mar 2026 19:42:29 -0700 (PDT) Received: from squeak.grove.modra.org ([2406:3400:51d:8cc0:e44d:e37d:834d:f1cc]) by smtp.gmail.com with ESMTPSA id 41be03b00d2f7-c769179e31asm2731334a12.17.2026.03.28.19.42.28 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 28 Mar 2026 19:42:28 -0700 (PDT) Received: by squeak.grove.modra.org (Postfix, from userid 1000) id 940511141940; Sun, 29 Mar 2026 13:12:25 +1030 (ACDT) Date: Sun, 29 Mar 2026 13:12:25 +1030 From: Alan Modra To: binutils@sourceware.org Subject: buffer overflow in loongarch_elf_add_sub_reloc_uleb128 Message-ID: MIME-Version: 1.0 Content-Disposition: inline X-Spam-Status: No, score=-3030.0 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, RCVD_IN_DNSWL_BLOCKED, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org X-BeenThere: binutils@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Binutils mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: binutils-bounces~patchwork=sourceware.org@sourceware.org oss-fuzz managed to trigger a buffer overflow processing a bogus leb128. Well, the leb128 encoding can be arbitrarily long so this isn't surprising at all. If we want to guard against user input triggering buffer overflows then we'd need to ensure input is terminated somehow, or do as this patch does. Remove _bfd_read_unsigned_leb128 and _bfd_read_signed_leb128, replacing all uses of these functions with _bfd_safe_read_leb128. * libbfd.c (_bfd_read_unsigned_leb128): Delete. (_bfd_read_signed_leb128): Delete. * libbfd-in.h: Remove declarations too. * libbfd.h: Regenerate. * elf32-msp430.c (msp430_final_link_relocate): Replace _bfd_read_unsigned_leb128 with _bfd_safe_read_leb128. * elf32-nds32.c (nds32_elf_relax_delete_blanks): Likewise. * elfnn-loongarch.c (perform_relocation): Likewise. (loongarch_elf_relocate_section): Likewise. * elfnn-riscv.c (perform_relocation): Likewise. * elfxx-loongarch.c (loongarch_elf_add_sub_reloc_uleb128): Likewise. (loongarch_write_unsigned_leb128): Make "len" a size_t. * elfxx-loongarch.h (loongarch_write_unsigned_leb128): Adjust. diff --git a/bfd/elf32-msp430.c b/bfd/elf32-msp430.c index d7d7730063f..5123a138b0b 100644 --- a/bfd/elf32-msp430.c +++ b/bfd/elf32-msp430.c @@ -893,13 +893,16 @@ msp430_final_link_relocate (reloc_howto_type * howto, bfd_byte *endp, *p; unsigned int val = relocation; - _bfd_read_unsigned_leb128 (input_bfd, contents + rel->r_offset, &len); + p = contents + rel->r_offset; + endp = contents + input_section->size; + _bfd_safe_read_leb128 (input_bfd, &p, false, endp); /* Clean the contents value to zero. Do not reduce the length. */ + endp = p - 1; p = contents + rel->r_offset; - endp = (p + len) - 1; + len = endp + 1 - p; memset (p, 0x80, len - 1); - *(endp) = 0; + *endp = 0; /* Get the length of the new uleb128 value. */ do diff --git a/bfd/elf32-nds32.c b/bfd/elf32-nds32.c index f308f2f16e1..2a76fd70462 100644 --- a/bfd/elf32-nds32.c +++ b/bfd/elf32-nds32.c @@ -9322,13 +9322,13 @@ nds32_elf_relax_delete_blanks (bfd *abfd, asection *sec, else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_DIFF_ULEB128 && isym[ELF32_R_SYM (irel->r_info)].st_shndx == sec_shndx) { - bfd_vma val = 0; - unsigned int len = 0; + bfd_vma val; unsigned long before, between; bfd_byte *endp, *p; - val = _bfd_read_unsigned_leb128 (abfd, contents + irel->r_offset, - &len); + p = contents + irel->r_offset; + endp = contents + sec->size; + val = _bfd_safe_read_leb128 (abfd, &p, false, endp); before = get_nds32_elf_blank_total (&blank_t, irel->r_addend, 0); between = get_nds32_elf_blank_total (&blank_t, @@ -9336,10 +9336,10 @@ nds32_elf_relax_delete_blanks (bfd *abfd, asection *sec, if (between == before) goto done_adjust_diff; + endp = p - 1; p = contents + irel->r_offset; - endp = p + len -1; - memset (p, 0x80, len); - *(endp) = 0; + memset (p, 0x80, endp - p); + *endp = 0; p = write_uleb128 (p, val - (between - before)) - 1; if (p < endp) *p |= 0x80; diff --git a/bfd/elfnn-loongarch.c b/bfd/elfnn-loongarch.c index 2c67cccd9fb..a7149691ffd 100644 --- a/bfd/elfnn-loongarch.c +++ b/bfd/elfnn-loongarch.c @@ -3110,9 +3110,11 @@ perform_relocation (const Elf_Internal_Rela *rel, asection *input_section, case R_LARCH_ADD_ULEB128: case R_LARCH_SUB_ULEB128: { - unsigned int len = 0; - /* Before write uleb128, first read it to get it's length. */ - _bfd_read_unsigned_leb128 (input_bfd, contents + rel->r_offset, &len); + /* Before write uleb128, first read it to get its length. */ + bfd_byte *p = contents + rel->r_offset; + bfd_byte *end = contents + input_section->size; + _bfd_safe_read_leb128 (input_bfd, &p, false, end); + size_t len = p - (contents + rel->r_offset); loongarch_write_unsigned_leb128 (contents + rel->r_offset, len, value); r = bfd_reloc_ok; break; @@ -3805,9 +3807,11 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, case R_LARCH_SUB_ULEB128: { /* Get the value and length of the uleb128 data. */ - unsigned int len = 0; - bfd_vma old_value = _bfd_read_unsigned_leb128 (input_bfd, - contents + rel->r_offset, &len); + bfd_byte *p = contents + rel->r_offset; + bfd_byte *end = contents + input_section->size; + bfd_vma old_value = _bfd_safe_read_leb128 (input_bfd, &p, + false, end); + size_t len = p - (contents + rel->r_offset); if (R_LARCH_ADD_ULEB128 == ELFNN_R_TYPE (rel->r_info)) relocation = old_value + relocation + rel->r_addend; diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c index d3963ec9a58..97b469bd7db 100644 --- a/bfd/elfnn-riscv.c +++ b/bfd/elfnn-riscv.c @@ -2069,20 +2069,22 @@ perform_relocation (const reloc_howto_type *howto, /* R_RISCV_SET_ULEB128 won't go into here. */ case R_RISCV_SUB_ULEB128: { - unsigned int len = 0; - _bfd_read_unsigned_leb128 (input_bfd, contents + rel->r_offset, &len); + bfd_byte *p = contents + rel->r_offset; + bfd_byte *endp = contents + input_section->size; + _bfd_safe_read_leb128 (input_bfd, &p, false, endp); /* Clean the contents value to zero (0x80), but keep the original length. */ - bfd_byte *p = contents + rel->r_offset; - bfd_byte *endp = p + len - 1; + endp = p - 1; + p = contents + rel->r_offset; + size_t len = endp + 1 - p; memset (p, 0x80, len - 1); - *(endp) = 0; + *endp = 0; - /* Make sure the length of the new uleb128 value within the + /* Make sure the length of the new uleb128 value fits within the original (available) length. */ - unsigned int new_len = 0; - unsigned int val_t = value; + size_t new_len = 0; + bfd_vma val_t = value; do { new_len++; diff --git a/bfd/elfxx-loongarch.c b/bfd/elfxx-loongarch.c index c65682e22b1..2dd470ddafb 100644 --- a/bfd/elfxx-loongarch.c +++ b/bfd/elfxx-loongarch.c @@ -2409,9 +2409,13 @@ loongarch_elf_add_sub_reloc_uleb128 (bfd *abfd, input_section, octets)) return bfd_reloc_outofrange; - unsigned int len = 0; - bfd_byte *p = data + reloc_entry->address; - bfd_vma old_value = _bfd_read_unsigned_leb128 (abfd, p, &len); + bfd_byte *contents = data; + bfd_byte *p = contents + octets; + bfd_byte *endp = contents + input_section->size; + bfd_vma old_value = _bfd_safe_read_leb128 (abfd, &p, false, endp); + endp = p; + p = contents + octets; + size_t len = endp - p; switch (howto->type) { @@ -2437,7 +2441,7 @@ loongarch_elf_add_sub_reloc_uleb128 (bfd *abfd, LEN is the uleb128 value length. Return a pointer to the byte following the last byte that was written. */ bfd_byte * -loongarch_write_unsigned_leb128 (bfd_byte *p, unsigned int len, bfd_vma value) +loongarch_write_unsigned_leb128 (bfd_byte *p, size_t len, bfd_vma value) { bfd_byte c; do diff --git a/bfd/elfxx-loongarch.h b/bfd/elfxx-loongarch.h index 3a6e8ea0950..400b7377ccd 100644 --- a/bfd/elfxx-loongarch.h +++ b/bfd/elfxx-loongarch.h @@ -43,8 +43,7 @@ void bfd_elf64_loongarch_set_data_segment_info (struct bfd_link_info *, int *); bfd_byte * -loongarch_write_unsigned_leb128 (bfd_byte *p, unsigned int len, bfd_vma value) - ATTRIBUTE_HIDDEN; +loongarch_write_unsigned_leb128 (bfd_byte *, size_t, bfd_vma) ATTRIBUTE_HIDDEN; /* TRUE if this is a PLT reference to a local IFUNC. */ #define PLT_LOCAL_IFUNC_P(INFO, H) \ diff --git a/bfd/libbfd-in.h b/bfd/libbfd-in.h index 4cc391fad47..7b4c93228a7 100644 --- a/bfd/libbfd-in.h +++ b/bfd/libbfd-in.h @@ -826,10 +826,6 @@ extern void bfd_section_already_linked_table_traverse (bool (*) (struct bfd_section_already_linked_hash_entry *, void *), void *) ATTRIBUTE_HIDDEN; -extern bfd_vma _bfd_read_unsigned_leb128 - (bfd *, bfd_byte *, unsigned int *) ATTRIBUTE_HIDDEN; -extern bfd_signed_vma _bfd_read_signed_leb128 - (bfd *, bfd_byte *, unsigned int *) ATTRIBUTE_HIDDEN; extern bfd_vma _bfd_safe_read_leb128 (bfd *, bfd_byte **, bool, const bfd_byte * const) ATTRIBUTE_HIDDEN; extern bfd_byte * _bfd_write_unsigned_leb128 diff --git a/bfd/libbfd.c b/bfd/libbfd.c index 6311ef49f6e..0e6a6cae779 100644 --- a/bfd/libbfd.c +++ b/bfd/libbfd.c @@ -1385,37 +1385,6 @@ bfd_generic_is_local_label_name (bfd *abfd, const char *name) return name[0] == locals_prefix; } -/* Helper function for reading uleb128 encoded data. */ - -bfd_vma -_bfd_read_unsigned_leb128 (bfd *abfd ATTRIBUTE_UNUSED, - bfd_byte *buf, - unsigned int *bytes_read_ptr) -{ - bfd_vma result; - unsigned int num_read; - unsigned int shift; - bfd_byte byte; - - result = 0; - shift = 0; - num_read = 0; - do - { - byte = bfd_get_8 (abfd, buf); - buf++; - num_read++; - if (shift < 8 * sizeof (result)) - { - result |= (((bfd_vma) byte & 0x7f) << shift); - shift += 7; - } - } - while (byte & 0x80); - *bytes_read_ptr = num_read; - return result; -} - /* Read in a LEB128 encoded value from ABFD starting at *PTR. If SIGN is true, return a signed LEB128 value. *PTR is incremented by the number of bytes read. @@ -1453,39 +1422,6 @@ _bfd_safe_read_leb128 (bfd *abfd ATTRIBUTE_UNUSED, return result; } -/* Helper function for reading sleb128 encoded data. */ - -bfd_signed_vma -_bfd_read_signed_leb128 (bfd *abfd ATTRIBUTE_UNUSED, - bfd_byte *buf, - unsigned int *bytes_read_ptr) -{ - bfd_vma result; - unsigned int shift; - unsigned int num_read; - bfd_byte byte; - - result = 0; - shift = 0; - num_read = 0; - do - { - byte = bfd_get_8 (abfd, buf); - buf ++; - num_read ++; - if (shift < 8 * sizeof (result)) - { - result |= (((bfd_vma) byte & 0x7f) << shift); - shift += 7; - } - } - while (byte & 0x80); - if (shift < 8 * sizeof (result) && (byte & 0x40)) - result |= (((bfd_vma) -1) << shift); - *bytes_read_ptr = num_read; - return result; -} - /* Write VAL in uleb128 format to P. END indicates the last byte of allocated space for the uleb128 value to fit in. diff --git a/bfd/libbfd.h b/bfd/libbfd.h index fdf3588e18e..dad2a7fbdd5 100644 --- a/bfd/libbfd.h +++ b/bfd/libbfd.h @@ -832,10 +832,6 @@ extern void bfd_section_already_linked_table_traverse (bool (*) (struct bfd_section_already_linked_hash_entry *, void *), void *) ATTRIBUTE_HIDDEN; -extern bfd_vma _bfd_read_unsigned_leb128 - (bfd *, bfd_byte *, unsigned int *) ATTRIBUTE_HIDDEN; -extern bfd_signed_vma _bfd_read_signed_leb128 - (bfd *, bfd_byte *, unsigned int *) ATTRIBUTE_HIDDEN; extern bfd_vma _bfd_safe_read_leb128 (bfd *, bfd_byte **, bool, const bfd_byte * const) ATTRIBUTE_HIDDEN; extern bfd_byte * _bfd_write_unsigned_leb128