/* Test that dlopen on a large-e_phnum DSO does not overflow the stack when
   called from a thread with PTHREAD_STACK_MIN stack size (BZ #26577).
   Copyright (C) 2026 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library 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
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <https://www.gnu.org/licenses/>.  */

/* Before the fix for BZ #26577, _dl_map_object_from_fd and open_verify both
   used alloca(e_phnum * sizeof(ElfW(Phdr))) when the program-header table did
   not fit in the initial filebuf read.  Calling dlopen from a thread created
   with PTHREAD_STACK_MIN stack size (typically 16 KB on Linux) would overflow
   the stack even for moderate e_phnum values, and catastrophically for the
   0x7FFF-header DSO used here.

   The crafted DSO (tst-bz26577-mod.so, generated by gen-tst-bz26577-mod.py)
   has e_phnum == 0x7FFF: one PT_LOAD covering the ELF header and the rest
   PT_NULL.  dlopen on it must return NULL (no PT_DYNAMIC) without triggering
   a stack overflow.  */

#include <dlfcn.h>
#include <limits.h>
#include <stdlib.h>
#include <support/check.h>
#include <support/support.h>
#include <support/xthread.h>

static void *
dlopen_thread (void *arg)
{
  /* Attempt to load the large-phnum module from a PTHREAD_STACK_MIN thread.
     With the old alloca-based code this overflows the stack; with the fix the
     load fails gracefully because the module has no PT_DYNAMIC.  */
  void *h = dlopen ((const char *) arg, RTLD_LAZY);
  TEST_VERIFY (h == NULL);
  return NULL;
}

static int
do_test (void)
{
  char *path = xasprintf ("%s/elf/tst-bz26577-mod.so", support_objdir_root);
  size_t stacksize =
#ifdef PTHREAD_STACK_MIN
    PTHREAD_STACK_MIN
#else
    support_small_thread_stack_size ();
#endif

  pthread_attr_t attr;
  xpthread_attr_init (&attr);
  xpthread_attr_setstacksize (&attr, stacksize);
  pthread_t thr = xpthread_create (&attr, dlopen_thread, path);
  xpthread_join (thr);
  xpthread_attr_destroy (&attr);

  free (path);
  return 0;
}

#include <support/test-driver.c>
