From patchwork Sat Jul 4 03:01:56 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: "H.J. Lu" X-Patchwork-Id: 138466 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 9D4924BA23E7 for ; Sat, 4 Jul 2026 03:03:15 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 9D4924BA23E7 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=fxNyDQ+8 X-Original-To: binutils@sourceware.org Delivered-To: binutils@sourceware.org Received: from mail-pl1-x630.google.com (mail-pl1-x630.google.com [IPv6:2607:f8b0:4864:20::630]) by sourceware.org (Postfix) with ESMTPS id 1C18F4BA23C3 for ; Sat, 4 Jul 2026 03:02:36 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 1C18F4BA23C3 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 1C18F4BA23C3 Authentication-Results: sourceware.org; arc=pass smtp.remote-ip=2607:f8b0:4864:20::630 ARC-Seal: i=2; a=rsa-sha256; d=sourceware.org; s=key; t=1783134156; cv=pass; b=O2aopTvC/8CaYDZsJhg2dk3551IqbaHExi+pGGv1SiNY3imTjFQ+fwLEWdyuMNF3lTazOp9MsZ9KQK5CZKuwQ74YzVMdOTW+Ta4ExWbkyj3KZmhPiJ17m0tFUjOCWi5+8BjPpZNgTq77OCZyd+AMDv1Y5hM7LllYbuEsBQwMxqo= ARC-Message-Signature: i=2; a=rsa-sha256; d=sourceware.org; s=key; t=1783134156; c=relaxed/simple; bh=mGsBuStmzpxFl+a0obNiQPpUGDbiRzvzyb3G2JA7tdc=; h=DKIM-Signature:MIME-Version:From:Date:Message-ID:Subject:To; b=Q9P/FF5JNe2zeY8veH4qMLEJQJg88uvVbDpmZGLJu0j/XJCxgOJWgNu8rCVggRxi+o3fCRaW7PE8UlbAHGy/Dzv6ImUtmltUhV8QqP2eh375unUBv6CtJBulLIU4X9sEr/BsBspjAlbmip3a5vnJDtd/IMoamugr7b/C39mj7K8= ARC-Authentication-Results: i=2; 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=fxNyDQ+8 DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 1C18F4BA23C3 Received: by mail-pl1-x630.google.com with SMTP id d9443c01a7336-2cae134bdc8so5202985ad.3 for ; Fri, 03 Jul 2026 20:02:36 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1783134155; cv=none; d=google.com; s=arc-20260327; b=H9yQs7hB4JEhQRCZ6xchIDkHYd5uIEg5MVnv99cnzBvCNI9ZCJFHratrQWexJYrVG2 KKjggZ4cKhckre6Lngid5trEAT4l9GrHMYq9glKjb/iYzxr/31cWj7MmeQnfa4op2AeD 7J/j2IadDvP+7pRhr5+Kpnt4CrJ23Sx7ICkbOsh3UCfhAwQpr8ObSqSayRZ1+CVP/tY7 0oOjq9p2fegRxpm7cyZZBGsDH88js2RkTgHHofi3902q2gh+XvbGz2U2jDLJiXvcMjkY dhqcYh/N8t/0b1IDvCNyn+Uv/gvtqrzlmjCsHapZup3zgs1Hb036H3sQIuZikSjYmHsa Pn2w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20260327; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:dkim-signature; bh=S3T3uJT9t44NbBnTDo7OxA0ZG6yA8IIcLuh8l+9uRdw=; fh=I+qhSY5ktf66CLZYyuJDnoh0Kvc4/Q3NDZkgrTQ/yn8=; b=XCxbl7X/5S2D+N+iIWd0+rofDCpqxx//hFcNT16SoD0GpLMeOv4JVRhwPvVtAIDuoj LjS32hgdF5mjn6Alzxe/YH2Xyel+A4grSav2zNmCf4pL6EXLEU3wQhYEbEveu9+nag32 iwnvsKhTb3ewVwfrsonw4GNDUks7g3JB7hrhHbsZdK5LKZ+K6/bbXGe4tx0ZdEVP6b1+ Bt9xqppvk1lb+iOfXyevT7R83pryjq1uQaFrxNv50uVpzVXVu88kzROhJ42ReBYx29Gz OToQ0XLxSXXdH5PPZxn/EWCpuzuA+buA8Xlcr3HZ027q3riZncMwAHv5l89bcIqJ2qsl aehg==; darn=sourceware.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1783134155; x=1783738955; darn=sourceware.org; h=content-type:cc:to:subject:message-id:date:from:in-reply-to :references:mime-version:from:to:cc:subject:date:message-id:reply-to :content-type; bh=S3T3uJT9t44NbBnTDo7OxA0ZG6yA8IIcLuh8l+9uRdw=; b=fxNyDQ+8Q/FZ/ZtuFuL4qF563aasPmkLb9IczohHvwFwPS1jMJCcxq8xR+fvTGMKF6 bquvH8vo8sLZCVYOyhkgEwTiPHgZTZz3wZL+C5MPvIvZPmRFhZmqVfG0xT0KJiTZjReU GsvXyAxJwHd8gnmVkxFvEoolr9gBdO9igHsPxdhlfkSyapXsweYP+Qy02qvW/Oea9uPO NrC5AWkSMy/kAFM/vvtkuyFyB565CXCE8FNyKZjn9cPvUw++dsjZ5z7i4C8s75c/hv4J uf0hR/ij512+mYSjx9a5KBBFTxyT9JaKb9RxyDlXEhO/AnlvAs4kmctlTjCVm/11Z0Pm XlNw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783134155; x=1783738955; h=content-type:cc:to:subject:message-id:date:from:in-reply-to :references:mime-version:x-gm-gg:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to:content-type; bh=S3T3uJT9t44NbBnTDo7OxA0ZG6yA8IIcLuh8l+9uRdw=; b=o5cv6kNbj+S4hrxf3D6Va4XIMO2Mfgf78FfUjUfzzJyIW97Eg1LjXtUUJ+P1FXHhg6 /x1F6IQI2du5zd9nkxquRFyn36X53XKeJp2sQVQmO+BLf/IrxG/eT8XvwLkR9YqU92fo wBAoymKZ1IPOvnbrknaot7ZjU8hqVLPau3Pr4AktijAOpew5ex4XplrN6rq94qRUvK1G +Fcg1wk6tE2gM2CZDauhN/NEn67Z77TXr4G+guOg96fFR+Tfjs42cJHFVpeqIqS/VyM1 yVCgLcYU7AjTuoOJVQHxBTKmp0fz83YoCZbKnwAKYkVm/wbIhVrnlxQG0+B86F8KXNIJ VdgA== X-Gm-Message-State: AOJu0YySTMoY3CcrwEmplHGiQk9ZEpSNTy6m3DiXEZCTL+7wHsS8TUbZ Ar9vrOJyXx8KLzpRuFz2q7UevoAXjjeT0frQ28/XNzZk9LFAfS69n6dHEBNxVqfuK7kEqEnrgO9 Oob/sytJPi+kMsNkCP1NzObEX0q5TUOw3aTpx0a7r8w== X-Gm-Gg: AfdE7cnqsN2Cqs2ktgwZxlr7gsF+GE/4jSfRINezfYnQ6oZrjP+M5EXdCGgqfN4Hwqx n5B8QkcKq6wPSAyIH8qfRb1jZRBUeJnNLxN0KiGCdlz6CrdYk/EqBeqEYq8mvvLsgeJnGIrsTrW nwvvqnxtHTAZMGeONTnCR+5sqEpthLHNDCWh8AQfB+C0r+AR7PEQbtU6O0ugQo/H5bWn3/Pyc4z ydPoBESDGtnRFx8g1kJYx6PAn1xi3o1vYCmUqukntb/AI6vXzIY/VS5FPlmKpUaq7UZWhwf X-Received: by 2002:a17:903:15c7:b0:2ca:e5f1:af90 with SMTP id d9443c01a7336-2cbb9eb4084mr15473775ad.22.1783134154732; Fri, 03 Jul 2026 20:02:34 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: From: "H.J. Lu" Date: Sat, 4 Jul 2026 11:01:56 +0800 X-Gm-Features: AVVi8CeqnJqkDQZTHnxG8WPXdRAapwqQrEs4pHSYW5TUCLxqR7-PipORpfvg_Nw Message-ID: Subject: [PATCH v2] readelf: Save and dump the original section header values To: Alan Modra Cc: Binutils , Nick Clifton , Jan Beulich X-Spam-Status: No, score=-3009.5 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP shortcircuit=no 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 On Sat, Jul 4, 2026 at 8:39 AM H.J. Lu wrote: > > On Sat, Jul 4, 2026 at 7:49 AM Alan Modra wrote: > > > > On Fri, Jul 03, 2026 at 10:30:14PM +0800, H.J. Lu wrote: > > > validate_section_info clears the garbage values in the section header > > > to avoid crash later. Save and dump the original section header values > > > to make the garbage values in the section header visible when dumping > > > section headers. > > > > I think this would be better done the other way around. ie. have a > > Elf_Internal_Shdr **sane_section_headers that is initialised to point > > at entries in section_headers, with sane_section_headers[i] allocated > > as necessary when needing to correct a bogus header. > > > > I have thought about something similar and decided against it > since filedata->section_headers is used in many places. However, > I can change filedata->orig_section_headers to on demand. > > -- > H.J. Changes in v2: 1. filedata->orig_section_headers is changed to on demand. From 83200d28daa3bc00bd761c71300017671f72807e Mon Sep 17 00:00:00 2001 From: "H.J. Lu" Date: Thu, 2 Jul 2026 10:14:10 +0800 Subject: [PATCH v2] readelf: Save and dump the original section header values validate_section_info clears the garbage values in the section header to avoid crash later. Save and dump the original section header values to make the garbage values in the section header visible when dumping section headers. Note: orig_section_headers, instead of sane_section_headers, is added to filedata since filedata->section_headers is used in many places. Replace filedata->section_headers with filedata->sane_section_headers requires a much bigger change. * readelf.c (filedata): Add orig_section_headers. (save_original_section_header_values): New. (validate_section_info): Add a pointer to the original section header and call save_original_section_header_values to save the original section header values before clearing the section header fields. (get_32bit_section_headers): Allocate the original section header buffer. Pass the original section header pointer to validate_section_info. (get_64bit_section_headers): Likewise. (process_section_headers): Dump the original section header values if they exist. (process_relocs): Pass a dummy original section pointer to validate_section_info. (free_filedata): Free filedata->orig_section_headers. * testsuite/binutils-all/corrupt-1.elf.bz2: New file. * testsuite/binutils-all/corrupt-1.r: Likewise. * testsuite/binutils-all/readelf.exp: Run corrupt-1.elf test. Signed-off-by: H.J. Lu --- binutils/readelf.c | 112 ++++++++++++++++-- .../testsuite/binutils-all/corrupt-1.elf.bz2 | Bin 0 -> 81 bytes binutils/testsuite/binutils-all/corrupt-1.r | 3 + binutils/testsuite/binutils-all/readelf.exp | 12 ++ 4 files changed, 117 insertions(+), 10 deletions(-) create mode 100644 binutils/testsuite/binutils-all/corrupt-1.elf.bz2 create mode 100644 binutils/testsuite/binutils-all/corrupt-1.r diff --git a/binutils/readelf.c b/binutils/readelf.c index 2826e0cf195..93b1080659e 100644 --- a/binutils/readelf.c +++ b/binutils/readelf.c @@ -282,6 +282,7 @@ typedef struct filedata uint64_t archive_file_size; /* Everything below this point is cleared out by free_filedata. */ Elf_Internal_Shdr * section_headers; + Elf_Internal_Shdr ** orig_section_headers; Elf_Internal_Phdr * program_headers; char * string_table; uint64_t string_table_length; @@ -7848,15 +7849,48 @@ offset_from_vma (Filedata * filedata, uint64_t vma, uint64_t size) return vma; } +/* Save the original section header values. */ + +static void +save_original_section_header_values (Elf_Internal_Shdr *internal, + Elf_Internal_Shdr **orig_internal, + const char *dynamic_tag, + unsigned int i) +{ + /* Return if the original section header values have been saved. */ + if (*orig_internal != NULL) + return; + + *orig_internal = (Elf_Internal_Shdr *) + malloc (sizeof (Elf_Internal_Shdr)); + if (*orig_internal == NULL) + { + if (dynamic_tag) + error (_("Out of memory reading dynamic tag %s\n"), + dynamic_tag); + else + error (_("Out of memory reading %u section headers\n"), i); + return; + } + + /* Save the original section header values. */ + **orig_internal = *internal; +} + + /* Valid section info and clear the invalid fields. */ static void -validate_section_info (Elf_Internal_Shdr *internal, unsigned int i, - Filedata *filedata, bool dynamic, bool probe) +validate_section_info (Elf_Internal_Shdr *internal, + Elf_Internal_Shdr **orig_internal, + unsigned int i, Filedata *filedata, bool dynamic, + bool probe) { const char *dynamic_tag = NULL; const char *dynamicsz_tag = NULL; const char *dynamicent_tag = NULL; + /* Clear the origin section header pointer. */ + *orig_internal = NULL; if (probe) return; @@ -7880,6 +7914,10 @@ validate_section_info (Elf_Internal_Shdr *internal, unsigned int i, { warn (_("Ignore the out of range sh_link value of %u for " "section %u\n"), internal->sh_link, i); + /* Save the original section header values before garbage + values are cleared. */ + save_original_section_header_values (internal, orig_internal, + NULL, i); internal->sh_link = 0; } @@ -7888,6 +7926,8 @@ validate_section_info (Elf_Internal_Shdr *internal, unsigned int i, { warn (_("Ignore the out of range sh_info value of %u for " "section %u\n"), internal->sh_info, i); + save_original_section_header_values (internal, orig_internal, + NULL, i); internal->sh_info = 0; } } @@ -7902,6 +7942,8 @@ validate_section_info (Elf_Internal_Shdr *internal, unsigned int i, warn (_("Ignore the out of range sh_entsize value of %" PRIu64 " for section %u\n"), (uint64_t) internal->sh_entsize, i); + save_original_section_header_values (internal, orig_internal, + dynamicent_tag, i); internal->sh_entsize = 0; } @@ -7916,6 +7958,8 @@ validate_section_info (Elf_Internal_Shdr *internal, unsigned int i, else warn (_("Ignore the out of range sh_offset value of %" PRId64 " for section %u\n"), sh_offset, i); + save_original_section_header_values (internal, orig_internal, + dynamic_tag, i); internal->sh_offset = 0; } @@ -7930,6 +7974,8 @@ validate_section_info (Elf_Internal_Shdr *internal, unsigned int i, PRIu64 " for section %u with sh_offset value of %" PRId64 "\n"), (uint64_t) internal->sh_size, i, sh_offset); + save_original_section_header_values (internal, orig_internal, + dynamicsz_tag, i); internal->sh_size = 0; } } @@ -7944,6 +7990,7 @@ get_32bit_section_headers (Filedata * filedata, bool probe) { Elf32_External_Shdr * shdrs; Elf_Internal_Shdr * internal; + Elf_Internal_Shdr ** orig_internal; unsigned int i; unsigned int size = filedata->file_header.e_shentsize; unsigned int num = probe ? 1 : filedata->file_header.e_shnum; @@ -7983,9 +8030,22 @@ get_32bit_section_headers (Filedata * filedata, bool probe) return false; } + filedata->orig_section_headers = (Elf_Internal_Shdr **) + cmalloc (num, sizeof (Elf_Internal_Shdr *)); + if (filedata->orig_section_headers == NULL) + { + if (!probe) + error (_("Out of memory reading %u section headers\n"), num); + free (shdrs); + free (filedata->section_headers); + filedata->section_headers = NULL; + return false; + } + + orig_internal = filedata->orig_section_headers; for (i = 0, internal = filedata->section_headers; i < num; - i++, internal++) + i++, internal++, orig_internal++) { internal->sh_name = BYTE_GET (shdrs[i].sh_name); internal->sh_type = BYTE_GET (shdrs[i].sh_type); @@ -7997,7 +8057,8 @@ get_32bit_section_headers (Filedata * filedata, bool probe) internal->sh_info = BYTE_GET (shdrs[i].sh_info); internal->sh_addralign = BYTE_GET (shdrs[i].sh_addralign); internal->sh_entsize = BYTE_GET (shdrs[i].sh_entsize); - validate_section_info (internal, i, filedata, false, probe); + validate_section_info (internal, orig_internal, i, filedata, + false, probe); } free (shdrs); @@ -8011,6 +8072,7 @@ get_64bit_section_headers (Filedata * filedata, bool probe) { Elf64_External_Shdr * shdrs; Elf_Internal_Shdr * internal; + Elf_Internal_Shdr ** orig_internal; unsigned int i; unsigned int size = filedata->file_header.e_shentsize; unsigned int num = probe ? 1 : filedata->file_header.e_shnum; @@ -8052,9 +8114,22 @@ get_64bit_section_headers (Filedata * filedata, bool probe) return false; } + filedata->orig_section_headers = (Elf_Internal_Shdr **) + cmalloc (num, sizeof (Elf_Internal_Shdr *)); + if (filedata->orig_section_headers == NULL) + { + if (!probe) + error (_("Out of memory reading %u section headers\n"), num); + free (shdrs); + free (filedata->section_headers); + filedata->section_headers = NULL; + return false; + } + + orig_internal = filedata->orig_section_headers; for (i = 0, internal = filedata->section_headers; i < num; - i++, internal++) + i++, internal++, orig_internal++) { internal->sh_name = BYTE_GET (shdrs[i].sh_name); internal->sh_type = BYTE_GET (shdrs[i].sh_type); @@ -8066,7 +8141,8 @@ get_64bit_section_headers (Filedata * filedata, bool probe) internal->sh_info = BYTE_GET (shdrs[i].sh_info); internal->sh_offset = BYTE_GET (shdrs[i].sh_offset); internal->sh_addralign = BYTE_GET (shdrs[i].sh_addralign); - validate_section_info (internal, i, filedata, false, probe); + validate_section_info (internal, orig_internal, i, filedata, + false, probe); } free (shdrs); @@ -9016,10 +9092,18 @@ process_section_headers (Filedata * filedata) if (do_section_details) printf (_(" Flags\n")); - for (i = 0, section = filedata->section_headers; + Elf_Internal_Shdr **orig_section = filedata->orig_section_headers; + Elf_Internal_Shdr *sec; + for (i = 0, sec = filedata->section_headers; i < filedata->file_header.e_shnum; - i++, section++) + i++, sec++, orig_section++) { + /* Dump the original section header values if they exist. */ + if (*orig_section) + section = *orig_section; + else + section = sec; + /* Run some sanity checks on the section header. */ /* Check the sh_link field. */ @@ -10197,16 +10281,18 @@ process_relocs (Filedata * filedata) uint64_t num_reloc; uint64_t *relrs = NULL; Elf_Internal_Shdr section = {}; + Elf_Internal_Shdr *orig_section; section.sh_offset = filedata->dynamic_info[DT_RELR]; section.sh_size = rel_size; section.sh_entsize = rel_entsz; section.sh_type = SHT_RELR; - validate_section_info (§ion, DT_RELR, filedata, - true, false); + validate_section_info (§ion, &orig_section, DT_RELR, + filedata, true, false); num_reloc = count_relr_relocations (filedata, §ion, &relrs); + free (orig_section); free (relrs); if (num_reloc == 0) continue; @@ -24847,6 +24933,12 @@ free_filedata (Filedata *filedata) free (filedata->program_interpreter); free (filedata->program_headers); free (filedata->section_headers); + if (filedata->orig_section_headers) + { + for (unsigned int i = 0; i < filedata->file_header.e_shnum; i++) + free (filedata->orig_section_headers[i]); + free (filedata->orig_section_headers); + } free (filedata->string_table); free (filedata->dump.dump_sects); free (filedata->dynamic_strings); diff --git a/binutils/testsuite/binutils-all/corrupt-1.elf.bz2 b/binutils/testsuite/binutils-all/corrupt-1.elf.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..e9c9f44343a4aadd90457df5e49923830881d194 GIT binary patch literal 81 zcmV-X0IvT+T4*^jL0KkKS`e0 nlM^7w$`z$$Gahz REL 0000000000000000 000000 6666666666666600 00 0 0 0 +#pass diff --git a/binutils/testsuite/binutils-all/readelf.exp b/binutils/testsuite/binutils-all/readelf.exp index 0f3d75090b2..6c5e63fb9e8 100644 --- a/binutils/testsuite/binutils-all/readelf.exp +++ b/binutils/testsuite/binutils-all/readelf.exp @@ -612,6 +612,18 @@ if ![is_remote host] { } else { readelf_test {-wi} $tempfile pr26160.r } + + set test $srcdir/$subdir/corrupt-1.elf.bz2 + # We need to strip the ".bz2", but can leave the dirname. + set t $subdir/[file tail $test] + set testname [file rootname $t] + verbose $testname + set tempfile tmpdir/corrupt-1.elf + if {[catch "system \"bzip2 -dc $test > $tempfile\""] != 0} { + untested "bzip2 -dc ($testname)" + } else { + readelf_test {-SW} $tempfile corrupt-1.r + } } # Check dwarf-5 support for DW_OP_addrx. -- 2.54.0