From 2ae7a8336634c0ca12262d3a2a0d3ec2237d9638 Mon Sep 17 00:00:00 2001
From: Karan Kurani <karankurani3k@gmail.com>
Date: Tue, 21 Jul 2026 10:08:34 +0000
Subject: [RFC PATCH 2/2] tests: Check elf_update layout arithmetic boundaries

Exercise ELF32 and ELF64 section, program-header, section-header, data
descriptor, alignment, and signed-result boundaries through public libelf
APIs.  Require arithmetic rejection to return -1 with ELF_E_RANGE and keep
exact valid ELF32 and INT64_MAX layout controls.

Also verify the exact automatic-layout results for d_align and sh_addralign
values zero and one after a nonzero running offset.  Check ELF_C_NULL
results, then write and reopen the outputs through public libelf APIs so the
compatibility cases validate offsets, section sizes, and serialized bytes.

Use an unbacked SHT_NOBITS descriptor, as supported by existing libelf tests,
to advance automatic layout beyond INT64_MAX without allocating or writing a
huge file.  Verify that the next descriptor's signed d_off remains unchanged
when elf_update rejects the layout with ELF_E_RANGE.

Without the preceding libelf change, the test observes wrapped sizes for
the section, program-header, and section-header families and fails the
range checks.  With it, all overflow cases are rejected before
serialization.

Signed-off-by: Karan Kurani <karankurani3k@gmail.com>
---
 tests/Makefile.am       |   6 +-
 tests/update-overflow.c | 604 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 608 insertions(+), 2 deletions(-)
 create mode 100644 tests/update-overflow.c

diff --git a/tests/Makefile.am b/tests/Makefile.am
index 8e6109f5..b16d79b1 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -33,7 +33,8 @@ tests_rpath = no
 endif
 
 check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \
-		  showptable update1 update2 update3 update4 test-nlist \
+		  showptable update1 update2 update3 update4 update-overflow \
+		  test-nlist \
 		  show-die-info get-files next-files get-lines next-lines \
 		  get-pubnames get-files-define-file \
 		  get-aranges allfcts line2addr addrscopes funcscopes \
@@ -123,7 +124,7 @@ test-nlist$(EXEEXT): test-nlist.c
 
 TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile test-nlist \
 	run-ar-N.sh \
-	update1 update2 update3 update4 \
+	update1 update2 update3 update4 update-overflow \
 	run-show-die-info.sh run-get-files.sh run-get-lines.sh \
 	run-next-files.sh run-next-lines.sh \
 	run-get-pubnames.sh run-get-aranges.sh run-allfcts.sh \
@@ -807,6 +808,7 @@ update1_LDADD = $(libelf)
 update2_LDADD = $(libelf)
 update3_LDADD = $(libdw) $(libelf)
 update4_LDADD = $(libdw) $(libelf)
+update_overflow_LDADD = $(libelf)
 show_die_info_LDADD = $(libdw) $(libelf)
 get_pubnames_LDADD = $(libdw) $(libelf)
 show_abbrev_LDADD = $(libdw) $(libelf)
diff --git a/tests/update-overflow.c b/tests/update-overflow.c
new file mode 100644
index 00000000..970d3bef
--- /dev/null
+++ b/tests/update-overflow.c
@@ -0,0 +1,604 @@
+/* Test overflow checking in elf_update layout calculations.
+   Copyright (C) 2026 Karan Kurani
+   This file is part of elfutils.
+
+   This file 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.
+
+   elfutils 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 <http://www.gnu.org/licenses/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <elf.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <inttypes.h>
+#include <libelf.h>
+#include <libelfP.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+
+enum layout_kind
+{
+  LAYOUT_SECTION,
+  LAYOUT_PHDR,
+  LAYOUT_SHDR,
+  LAYOUT_DATA,
+  LAYOUT_NEGATIVE_DATA,
+  LAYOUT_ALIGN
+};
+
+struct layout_result
+{
+  int64_t size;
+  int error;
+};
+
+static int failures;
+
+static void
+fail (const char *what)
+{
+  fprintf (stderr, "%s: %s\n", what, elf_errmsg (-1));
+  exit (1);
+}
+
+static struct layout_result
+layout_size (int elfclass, enum layout_kind kind, uint64_t offset,
+	     uint64_t extent)
+{
+  char fname[] = "update-overflow.XXXXXX";
+  int fd = mkstemp (fname);
+  if (fd < 0)
+    fail ("mkstemp");
+  unlink (fname);
+
+  Elf *elf = elf_begin (fd, ELF_C_WRITE, NULL);
+  if (elf == NULL)
+    fail ("elf_begin");
+  if (gelf_newehdr (elf, elfclass) == 0)
+    fail ("gelf_newehdr");
+
+  GElf_Ehdr ehdr;
+  if (gelf_getehdr (elf, &ehdr) == NULL)
+    fail ("gelf_getehdr");
+  ehdr.e_type = ET_REL;
+  ehdr.e_machine = elfclass == ELFCLASS32 ? EM_386 : EM_X86_64;
+  ehdr.e_version = EV_CURRENT;
+
+  static unsigned char byte;
+  if (kind != LAYOUT_PHDR)
+    {
+      Elf_Scn *scn = elf_newscn (elf);
+      if (scn == NULL)
+	fail ("elf_newscn");
+
+      GElf_Shdr shdr = { 0 };
+      shdr.sh_type = (kind == LAYOUT_SECTION ? SHT_PROGBITS : SHT_NOBITS);
+      shdr.sh_addralign = kind == LAYOUT_ALIGN ? offset : 1;
+      shdr.sh_offset = kind == LAYOUT_ALIGN ? 0 : offset;
+      shdr.sh_size = extent;
+      if (gelf_update_shdr (scn, &shdr) == 0)
+	fail ("gelf_update_shdr");
+
+      if (kind == LAYOUT_SECTION || kind == LAYOUT_DATA
+	  || kind == LAYOUT_NEGATIVE_DATA)
+	{
+	  Elf_Data *data = elf_newdata (scn);
+	  if (data == NULL)
+	    fail ("elf_newdata");
+	  data->d_buf = &byte;
+	  data->d_off = (kind == LAYOUT_DATA
+			 ? (int64_t) offset
+			 : kind == LAYOUT_NEGATIVE_DATA ? -1 : 0);
+	  data->d_size = extent;
+	  data->d_align = 1;
+	  data->d_type = ELF_T_BYTE;
+	  data->d_version = EV_CURRENT;
+	}
+    }
+  else
+    {
+      if (gelf_newphdr (elf, 1) == 0)
+	fail ("gelf_newphdr");
+      GElf_Phdr phdr = { .p_type = PT_NULL };
+      if (gelf_update_phdr (elf, 0, &phdr) == 0)
+	fail ("gelf_update_phdr");
+      if (gelf_getehdr (elf, &ehdr) == NULL)
+	fail ("gelf_getehdr after gelf_newphdr");
+      ehdr.e_type = ET_REL;
+      ehdr.e_machine = elfclass == ELFCLASS32 ? EM_386 : EM_X86_64;
+      ehdr.e_version = EV_CURRENT;
+      ehdr.e_phoff = offset;
+    }
+
+  if (kind == LAYOUT_SHDR)
+    ehdr.e_shoff = offset;
+  else if (kind != LAYOUT_PHDR && kind != LAYOUT_ALIGN)
+    ehdr.e_shoff = 256;
+
+  if (gelf_update_ehdr (elf, &ehdr) == 0)
+    fail ("gelf_update_ehdr");
+  if (kind != LAYOUT_ALIGN
+      && elf_flagelf (elf, ELF_C_SET, ELF_F_LAYOUT) == 0)
+    fail ("elf_flagelf");
+
+  /* Setup helpers may leave a non-fatal error behind.  Only the error from
+     the elf_update call below is relevant to a rejected layout.  */
+  (void) elf_errno ();
+  struct layout_result result;
+  result.size = elf_update (elf, ELF_C_NULL);
+  result.error = elf_errno ();
+  elf_end (elf);
+  close (fd);
+  return result;
+}
+
+static void
+check_size (const char *name, struct layout_result result, int64_t expected)
+{
+  if (result.size != expected)
+    {
+      fprintf (stderr, "%s: got %" PRId64 ", expected %" PRId64 "\n",
+	       name, result.size, expected);
+      ++failures;
+    }
+}
+
+static void
+check_range (const char *name, struct layout_result result)
+{
+  if (result.size != -1 || result.error != ELF_E_RANGE)
+    {
+      const char *message = elf_errmsg (result.error);
+      fprintf (stderr, "%s: got size %" PRId64 ", error %d (%s); "
+	       "expected -1 and ELF_E_RANGE\n", name, result.size,
+	       result.error, message == NULL ? "no error" : message);
+      ++failures;
+    }
+}
+
+static void
+set_data (Elf_Data *data, void *buffer, size_t size, size_t align)
+{
+  data->d_buf = buffer;
+  data->d_size = size;
+  data->d_align = align;
+  data->d_type = ELF_T_BYTE;
+  data->d_version = EV_CURRENT;
+}
+
+static void
+check_automatic_signed_data_offset (void)
+{
+  if (SIZE_MAX <= INT64_MAX)
+    return;
+
+  char fname[] = "update-overflow-signed-data.XXXXXX";
+  int fd = mkstemp (fname);
+  if (fd < 0)
+    fail ("signed data mkstemp");
+  unlink (fname);
+
+  Elf *elf = elf_begin (fd, ELF_C_WRITE, NULL);
+  if (elf == NULL || gelf_newehdr (elf, ELFCLASS64) == 0)
+    fail ("signed data create ELF header");
+
+  GElf_Ehdr ehdr;
+  if (gelf_getehdr (elf, &ehdr) == NULL)
+    fail ("signed data gelf_getehdr");
+  ehdr.e_type = ET_REL;
+  ehdr.e_machine = EM_X86_64;
+  ehdr.e_version = EV_CURRENT;
+  if (gelf_update_ehdr (elf, &ehdr) == 0)
+    fail ("signed data gelf_update_ehdr");
+
+  Elf_Scn *scn = elf_newscn (elf);
+  if (scn == NULL)
+    fail ("signed data elf_newscn");
+  GElf_Shdr shdr = { 0 };
+  shdr.sh_type = SHT_NOBITS;
+  shdr.sh_addralign = 1;
+  if (gelf_update_shdr (scn, &shdr) == 0)
+    fail ("signed data gelf_update_shdr");
+
+  Elf_Data *first_data = elf_newdata (scn);
+  Elf_Data *second_data = elf_newdata (scn);
+  if (first_data == NULL || second_data == NULL)
+    fail ("signed data elf_newdata");
+
+  size_t huge_size = (size_t) INT64_MAX;
+  ++huge_size;
+  set_data (first_data, NULL, huge_size, 1);
+  set_data (second_data, NULL, 1, 1);
+  first_data->d_off = 11;
+  second_data->d_off = 77;
+
+  for (int attempt = 0; attempt < 2; ++attempt)
+    {
+      (void) elf_errno ();
+      int64_t result = elf_update (elf, ELF_C_NULL);
+      int error = elf_errno ();
+      if (result != -1 || error != ELF_E_RANGE
+	  || first_data->d_off != 0 || second_data->d_off != 77)
+	{
+	  fprintf (stderr, "ELF64 automatic signed data offset attempt %d: "
+		   "got size %" PRId64 ", error %d, offsets %" PRId64
+		   "/%" PRId64 "; expected -1, ELF_E_RANGE, 0/77\n",
+		   attempt + 1, result, error, first_data->d_off,
+		   second_data->d_off);
+	  ++failures;
+	}
+
+      if (gelf_getehdr (elf, &ehdr) == NULL
+	  || gelf_getshdr (scn, &shdr) == NULL)
+	fail ("signed data get headers after elf_update");
+      if (ehdr.e_shoff != 0 || shdr.sh_offset != 0 || shdr.sh_size != 0)
+	{
+	  fprintf (stderr, "ELF64 automatic signed data offset attempt %d: "
+		   "headers changed to e_shoff=%" PRIu64
+		   ", sh_offset=%" PRIu64 ", sh_size=%" PRIu64 "\n",
+		   attempt + 1, (uint64_t) ehdr.e_shoff,
+		   (uint64_t) shdr.sh_offset, (uint64_t) shdr.sh_size);
+	  ++failures;
+	}
+    }
+
+  if (elf_end (elf) != 0)
+    fail ("signed data elf_end");
+  close (fd);
+}
+
+static void
+check_alignment (int elfclass, size_t align)
+{
+  char fname[64];
+  snprintf (fname, sizeof fname, "update-overflow-align-%d-%zu.XXXXXX",
+	    elfclass, align);
+  int fd = mkstemp (fname);
+  if (fd < 0)
+    fail ("alignment mkstemp");
+
+  Elf *elf = elf_begin (fd, ELF_C_WRITE, NULL);
+  if (elf == NULL || gelf_newehdr (elf, elfclass) == 0)
+    fail ("alignment create ELF header");
+
+  GElf_Ehdr ehdr;
+  if (gelf_getehdr (elf, &ehdr) == NULL)
+    fail ("alignment gelf_getehdr");
+  ehdr.e_type = ET_REL;
+  ehdr.e_machine = elfclass == ELFCLASS32 ? EM_386 : EM_X86_64;
+  ehdr.e_version = EV_CURRENT;
+
+  Elf_Scn *data_scn = elf_newscn (elf);
+  if (data_scn == NULL)
+    fail ("alignment elf_newscn data");
+  GElf_Shdr data_shdr = { 0 };
+  data_shdr.sh_name = 1;
+  data_shdr.sh_type = SHT_PROGBITS;
+  data_shdr.sh_flags = SHF_ALLOC | SHF_WRITE;
+  data_shdr.sh_addralign = 1;
+  if (gelf_update_shdr (data_scn, &data_shdr) == 0)
+    fail ("alignment gelf_update_shdr data");
+
+  static unsigned char first[] = { 0x11, 0x22, 0x33 };
+  static unsigned char second[] = { 0x44, 0x55 };
+  Elf_Data *first_data = elf_newdata (data_scn);
+  Elf_Data *second_data = elf_newdata (data_scn);
+  if (first_data == NULL || second_data == NULL)
+    fail ("alignment elf_newdata");
+  set_data (first_data, first, sizeof first, 1);
+  set_data (second_data, second, sizeof second, align);
+
+  Elf_Scn *str_scn = elf_newscn (elf);
+  if (str_scn == NULL)
+    fail ("alignment elf_newscn shstrtab");
+  GElf_Shdr str_shdr = { 0 };
+  str_shdr.sh_name = 7;
+  str_shdr.sh_type = SHT_STRTAB;
+  str_shdr.sh_addralign = 1;
+  if (gelf_update_shdr (str_scn, &str_shdr) == 0)
+    fail ("alignment gelf_update_shdr shstrtab");
+  static char names[] = "\0.data\0.shstrtab";
+  Elf_Data *str_data = elf_newdata (str_scn);
+  if (str_data == NULL)
+    fail ("alignment elf_newdata shstrtab");
+  set_data (str_data, names, sizeof names, 1);
+
+  ehdr.e_shstrndx = 2;
+  if (gelf_update_ehdr (elf, &ehdr) == 0)
+    fail ("alignment gelf_update_ehdr");
+
+  static const unsigned char expected_zero[] = { 0x44, 0x55 };
+  static const unsigned char expected_one[] = {
+    0x11, 0x22, 0x33, 0x44, 0x55
+  };
+  const unsigned char *expected = align == 0 ? expected_zero : expected_one;
+  size_t expected_size = align == 0 ? sizeof expected_zero : sizeof expected_one;
+  int64_t expected_offset = align == 0 ? 0 : sizeof first;
+
+  (void) elf_errno ();
+  int64_t null_size = elf_update (elf, ELF_C_NULL);
+  int null_error = elf_errno ();
+  int64_t null_offset = second_data->d_off;
+  if (gelf_getshdr (data_scn, &data_shdr) == NULL)
+    fail ("alignment gelf_getshdr after ELF_C_NULL");
+  uint64_t null_section_size = data_shdr.sh_size;
+
+  (void) elf_errno ();
+  int64_t size = elf_update (elf, ELF_C_WRITE);
+  int error = elf_errno ();
+  int64_t second_offset = second_data->d_off;
+  if (elf_end (elf) != 0)
+    fail ("alignment elf_end");
+  close (fd);
+
+  bool valid = (null_size > 0 && null_offset == expected_offset
+		&& null_section_size == expected_size && size > 0
+		&& second_offset == expected_offset);
+  fd = open (fname, O_RDONLY);
+  if (fd < 0)
+    fail ("alignment reopen");
+  elf = elf_begin (fd, ELF_C_READ, NULL);
+  if (elf == NULL)
+    fail ("alignment elf_begin reopen");
+
+  size_t shnum;
+  size_t shstrndx;
+  Elf_Scn *scn = elf_getscn (elf, 1);
+  GElf_Shdr shdr;
+  Elf_Data *data = scn == NULL ? NULL : elf_getdata (scn, NULL);
+  valid = (valid && gelf_getclass (elf) == elfclass
+	   && elf_getshdrnum (elf, &shnum) == 0 && shnum == 3
+	   && elf_getshdrstrndx (elf, &shstrndx) == 0 && shstrndx == 2
+	   && scn != NULL && gelf_getshdr (scn, &shdr) != NULL
+	   && shdr.sh_type == SHT_PROGBITS
+	   && shdr.sh_size == expected_size
+	   && data != NULL && data->d_size == expected_size
+	   && memcmp (data->d_buf, expected, expected_size) == 0);
+  elf_end (elf);
+  close (fd);
+
+  if (! valid)
+    {
+      const char *message = elf_errmsg (error);
+      fprintf (stderr, "ELF%d d_align=%zu: null=%" PRId64
+	       ", null_error=%d, null d_off=%" PRId64
+	       ", null sh_size=%" PRIu64 ", write=%" PRId64
+	       ", error=%d (%s), write d_off=%" PRId64
+	       "; output validation failed\n",
+	       elfclass == ELFCLASS32 ? 32 : 64, align, null_size,
+	       null_error, null_offset, null_section_size, size, error,
+	       message == NULL ? "no error" : message, second_offset);
+      ++failures;
+    }
+
+  if (getenv ("ELFUTILS_TEST_KEEP_OUTPUT") != NULL)
+    printf ("kept %s\n", fname);
+  else
+    unlink (fname);
+}
+
+static void
+check_section_alignment (int elfclass, size_t align)
+{
+  char fname[72];
+  snprintf (fname, sizeof fname, "update-overflow-shalign-%d-%zu.XXXXXX",
+	    elfclass, align);
+  int fd = mkstemp (fname);
+  if (fd < 0)
+    fail ("section alignment mkstemp");
+
+  Elf *elf = elf_begin (fd, ELF_C_WRITE, NULL);
+  if (elf == NULL || gelf_newehdr (elf, elfclass) == 0)
+    fail ("section alignment create ELF header");
+
+  GElf_Ehdr ehdr;
+  if (gelf_getehdr (elf, &ehdr) == NULL)
+    fail ("section alignment gelf_getehdr");
+  ehdr.e_type = ET_REL;
+  ehdr.e_machine = elfclass == ELFCLASS32 ? EM_386 : EM_X86_64;
+  ehdr.e_version = EV_CURRENT;
+
+  static unsigned char first[] = { 0x11, 0x22, 0x33 };
+  static unsigned char second[] = { 0x44, 0x55 };
+  Elf_Scn *first_scn = elf_newscn (elf);
+  if (first_scn == NULL)
+    fail ("section alignment elf_newscn first");
+  GElf_Shdr first_shdr = { 0 };
+  first_shdr.sh_name = 1;
+  first_shdr.sh_type = SHT_PROGBITS;
+  first_shdr.sh_addralign = 1;
+  if (gelf_update_shdr (first_scn, &first_shdr) == 0)
+    fail ("section alignment gelf_update_shdr first");
+  Elf_Data *first_data = elf_newdata (first_scn);
+  if (first_data == NULL)
+    fail ("section alignment elf_newdata first");
+  set_data (first_data, first, sizeof first, 1);
+
+  Elf_Scn *second_scn = elf_newscn (elf);
+  if (second_scn == NULL)
+    fail ("section alignment elf_newscn second");
+  GElf_Shdr second_shdr = { 0 };
+  second_shdr.sh_name = 8;
+  second_shdr.sh_type = SHT_PROGBITS;
+  second_shdr.sh_addralign = align;
+  if (gelf_update_shdr (second_scn, &second_shdr) == 0)
+    fail ("section alignment gelf_update_shdr second");
+  Elf_Data *second_data = elf_newdata (second_scn);
+  if (second_data == NULL)
+    fail ("section alignment elf_newdata second");
+  set_data (second_data, second, sizeof second, 1);
+
+  Elf_Scn *str_scn = elf_newscn (elf);
+  if (str_scn == NULL)
+    fail ("section alignment elf_newscn shstrtab");
+  GElf_Shdr str_shdr = { 0 };
+  str_shdr.sh_name = 16;
+  str_shdr.sh_type = SHT_STRTAB;
+  str_shdr.sh_addralign = 1;
+  if (gelf_update_shdr (str_scn, &str_shdr) == 0)
+    fail ("section alignment gelf_update_shdr shstrtab");
+  static char names[] = "\0.first\0.second\0.shstrtab";
+  Elf_Data *str_data = elf_newdata (str_scn);
+  if (str_data == NULL)
+    fail ("section alignment elf_newdata shstrtab");
+  set_data (str_data, names, sizeof names, 1);
+
+  ehdr.e_shstrndx = 3;
+  if (gelf_update_ehdr (elf, &ehdr) == 0)
+    fail ("section alignment gelf_update_ehdr");
+
+  uint64_t expected_offset = (elfclass == ELFCLASS32
+			      ? sizeof (Elf32_Ehdr) : sizeof (Elf64_Ehdr))
+			     + sizeof first;
+  int64_t expected_size = elfclass == ELFCLASS32 ? 244 : 352;
+
+  (void) elf_errno ();
+  int64_t null_size = elf_update (elf, ELF_C_NULL);
+  int null_error = elf_errno ();
+  if (gelf_getshdr (second_scn, &second_shdr) == NULL)
+    fail ("section alignment gelf_getshdr after ELF_C_NULL");
+  uint64_t null_offset = second_shdr.sh_offset;
+  uint64_t null_align = second_shdr.sh_addralign;
+
+  (void) elf_errno ();
+  int64_t size = elf_update (elf, ELF_C_WRITE);
+  int error = elf_errno ();
+  if (gelf_getshdr (second_scn, &second_shdr) == NULL)
+    fail ("section alignment gelf_getshdr after ELF_C_WRITE");
+  uint64_t write_offset = second_shdr.sh_offset;
+  uint64_t write_align = second_shdr.sh_addralign;
+  if (elf_end (elf) != 0)
+    fail ("section alignment elf_end");
+  close (fd);
+
+  bool valid = (null_size == expected_size && null_offset == expected_offset
+		&& null_align == 1 && size == expected_size
+		&& write_offset == expected_offset && write_align == 1);
+  fd = open (fname, O_RDONLY);
+  if (fd < 0)
+    fail ("section alignment reopen");
+  elf = elf_begin (fd, ELF_C_READ, NULL);
+  if (elf == NULL)
+    fail ("section alignment elf_begin reopen");
+
+  size_t shnum;
+  size_t shstrndx;
+  Elf_Scn *scn = elf_getscn (elf, 2);
+  GElf_Shdr shdr;
+  Elf_Data *data = scn == NULL ? NULL : elf_getdata (scn, NULL);
+  valid = (valid && gelf_getclass (elf) == elfclass
+	   && elf_getshdrnum (elf, &shnum) == 0 && shnum == 4
+	   && elf_getshdrstrndx (elf, &shstrndx) == 0 && shstrndx == 3
+	   && scn != NULL && gelf_getshdr (scn, &shdr) != NULL
+	   && shdr.sh_offset == expected_offset && shdr.sh_addralign == 1
+	   && shdr.sh_size == sizeof second
+	   && data != NULL && data->d_size == sizeof second
+	   && memcmp (data->d_buf, second, sizeof second) == 0);
+  elf_end (elf);
+  close (fd);
+
+  if (! valid)
+    {
+      const char *message = elf_errmsg (error);
+      fprintf (stderr, "ELF%d sh_addralign=%zu: null=%" PRId64
+	       ", null_error=%d, null offset=%" PRIu64
+	       ", null align=%" PRIu64 ", write=%" PRId64
+	       ", error=%d (%s), write offset=%" PRIu64
+	       ", write align=%" PRIu64 "; output validation failed\n",
+	       elfclass == ELFCLASS32 ? 32 : 64, align, null_size,
+	       null_error, null_offset, null_align, size, error,
+	       message == NULL ? "no error" : message, write_offset,
+	       write_align);
+      ++failures;
+    }
+
+  if (getenv ("ELFUTILS_TEST_KEEP_OUTPUT") != NULL)
+    printf ("kept %s\n", fname);
+  else
+    unlink (fname);
+}
+
+int
+main (void)
+{
+  if (elf_version (EV_CURRENT) == EV_NONE)
+    fail ("elf_version");
+
+  /* ELF32 fields can describe a file extent just beyond UINT32_MAX.
+     Keep that valid boundary while doing the calculation in uint64_t.  */
+  check_size ("ELF32 section boundary",
+	      layout_size (ELFCLASS32, LAYOUT_SECTION, UINT32_MAX, 1),
+	      (int64_t) UINT32_MAX + 1);
+  check_size ("ELF32 program header boundary",
+	      layout_size (ELFCLASS32, LAYOUT_PHDR, UINT32_MAX - 15, 0),
+	      (int64_t) UINT32_MAX + 17);
+  check_size ("ELF32 section header boundary",
+	      layout_size (ELFCLASS32, LAYOUT_SHDR, UINT32_MAX - 31, 1),
+	      (int64_t) UINT32_MAX + 49);
+  check_range ("ELF32 negative data offset",
+	       layout_size (ELFCLASS32, LAYOUT_NEGATIVE_DATA, 0, 1));
+
+  /* INT64_MAX itself remains a valid result for each explicit ELF64
+     extent family.  ELF_C_NULL makes these boundary checks without creating
+     sparse multi-exabyte files.  */
+  check_size ("ELF64 exact section boundary",
+	      layout_size (ELFCLASS64, LAYOUT_SECTION, INT64_MAX - 1, 1),
+	      INT64_MAX);
+  check_size ("ELF64 exact program header boundary",
+	      layout_size (ELFCLASS64, LAYOUT_PHDR, INT64_MAX - 56, 0),
+	      INT64_MAX);
+  check_size ("ELF64 exact section header boundary",
+	      layout_size (ELFCLASS64, LAYOUT_SHDR, INT64_MAX - 128, 1),
+	      INT64_MAX);
+
+  /* Extents which wrap uint64_t or elf_update's int64_t result must be
+     rejected before the output file is allocated or mapped.  */
+  check_range ("ELF64 section overflow",
+	       layout_size (ELFCLASS64, LAYOUT_SECTION, UINT64_MAX, 1));
+  check_range ("ELF64 program header overflow",
+	       layout_size (ELFCLASS64, LAYOUT_PHDR, UINT64_MAX - 15, 0));
+  check_range ("ELF64 section header overflow",
+	       layout_size (ELFCLASS64, LAYOUT_SHDR, UINT64_MAX - 31, 1));
+  check_range ("ELF64 signed result overflow",
+	       layout_size (ELFCLASS64, LAYOUT_SECTION, INT64_MAX, 1));
+  check_range ("ELF64 data descriptor overflow",
+	       layout_size (ELFCLASS64, LAYOUT_DATA, INT64_MAX, UINT64_MAX));
+  check_range ("ELF64 negative data offset",
+	       layout_size (ELFCLASS64, LAYOUT_NEGATIVE_DATA, 0, 1));
+  check_range ("ELF64 alignment overflow",
+	       layout_size (ELFCLASS64, LAYOUT_ALIGN,
+		    UINT64_C (1) << 63, 1));
+  check_automatic_signed_data_offset ();
+
+  check_size ("ELF64 valid layout",
+	      layout_size (ELFCLASS64, LAYOUT_SECTION, 4096, 1), 4097);
+
+  /* Preserve the distinct historical effects of d_align zero and one after a
+     three-byte descriptor, and sh_addralign zero being normalized to one.  */
+  check_alignment (ELFCLASS32, 0);
+  check_alignment (ELFCLASS32, 1);
+  check_alignment (ELFCLASS64, 0);
+  check_alignment (ELFCLASS64, 1);
+  check_section_alignment (ELFCLASS32, 0);
+  check_section_alignment (ELFCLASS32, 1);
+  check_section_alignment (ELFCLASS64, 0);
+  check_section_alignment (ELFCLASS64, 1);
+
+  return failures != 0;
+}
-- 
2.44.0

