Blame dlfcn/tst-rec-dlopen.c

Packit 6c4009
/* Test recursive dlopen using malloc hooks.
Packit 6c4009
   Copyright (C) 2015-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <dlfcn.h>
Packit 6c4009
#include <stdbool.h>
Packit 6c4009
#include <stdalign.h>
Packit 6c4009
#include <sys/mman.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
Packit 6c4009
#define DSO "moddummy1.so"
Packit 6c4009
#define FUNC "dummy1"
Packit 6c4009
Packit 6c4009
#define DSO1 "moddummy2.so"
Packit 6c4009
#define FUNC1 "dummy2"
Packit 6c4009
Packit 6c4009
/* Result of the called function.  */
Packit 6c4009
int func_result;
Packit 6c4009
Packit 6c4009
/* Call function func_name in DSO dso_name via dlopen.  */
Packit 6c4009
void
Packit 6c4009
call_func (const char *dso_name, const char *func_name)
Packit 6c4009
{
Packit 6c4009
  int ret;
Packit 6c4009
  void *dso;
Packit 6c4009
  int (*func) (void);
Packit 6c4009
  char *err;
Packit 6c4009
Packit 6c4009
  /* Open the DSO.  */
Packit 6c4009
  dso = dlopen (dso_name, RTLD_NOW|RTLD_GLOBAL);
Packit 6c4009
  if (dso == NULL)
Packit 6c4009
    {
Packit 6c4009
      err = dlerror ();
Packit 6c4009
      fprintf (stderr, "%s\n", err);
Packit 6c4009
      exit (1);
Packit 6c4009
    }
Packit 6c4009
  /* Clear any errors.  */
Packit 6c4009
  dlerror ();
Packit 6c4009
Packit 6c4009
  /* Lookup func.  */
Packit 6c4009
  func = (int (*) (void)) dlsym (dso, func_name);
Packit 6c4009
  if (func == NULL)
Packit 6c4009
    {
Packit 6c4009
      err = dlerror ();
Packit 6c4009
      if (err != NULL)
Packit 6c4009
        {
Packit 6c4009
	  fprintf (stderr, "%s\n", err);
Packit 6c4009
	  exit (1);
Packit 6c4009
        }
Packit 6c4009
    }
Packit 6c4009
  /* Call func.  */
Packit 6c4009
  func_result = (*func) ();
Packit 6c4009
Packit 6c4009
  /* Close the library and look for errors too.  */
Packit 6c4009
  ret = dlclose (dso);
Packit 6c4009
  if (ret != 0)
Packit 6c4009
    {
Packit 6c4009
      err = dlerror ();
Packit 6c4009
      fprintf (stderr, "%s\n", err);
Packit 6c4009
      exit (1);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* If true, call another function from malloc.  */
Packit 6c4009
static bool call_function;
Packit 6c4009
Packit 6c4009
/* Set to true to indicate that the interposed malloc was called.  */
Packit 6c4009
static bool interposed_malloc_called;
Packit 6c4009
Packit 6c4009
/* Interposed malloc which optionally calls another function.  */
Packit 6c4009
void *
Packit 6c4009
malloc (size_t size)
Packit 6c4009
{
Packit 6c4009
  interposed_malloc_called = true;
Packit 6c4009
  static void *(*original_malloc) (size_t);
Packit 6c4009
Packit 6c4009
  if (original_malloc == NULL)
Packit 6c4009
    {
Packit 6c4009
      static bool in_initialization;
Packit 6c4009
      if (in_initialization)
Packit 6c4009
	{
Packit 6c4009
	  const char *message
Packit 6c4009
	    = "error: malloc called recursively during initialization\n";
Packit 6c4009
	  (void) write (STDOUT_FILENO, message, strlen (message));
Packit 6c4009
	  _exit (2);
Packit 6c4009
	}
Packit 6c4009
      in_initialization = true;
Packit 6c4009
Packit 6c4009
      original_malloc
Packit 6c4009
	= (__typeof (original_malloc)) dlsym (RTLD_NEXT, "malloc");
Packit 6c4009
      if (original_malloc == NULL)
Packit 6c4009
	{
Packit 6c4009
	  const char *message
Packit 6c4009
	    = "error: dlsym for malloc failed\n";
Packit 6c4009
	  (void) write (STDOUT_FILENO, message, strlen (message));
Packit 6c4009
	  _exit (2);
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (call_function)
Packit 6c4009
    {
Packit 6c4009
      call_function = false;
Packit 6c4009
      call_func (DSO1, FUNC1);
Packit 6c4009
      call_function = true;
Packit 6c4009
    }
Packit 6c4009
  return original_malloc (size);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
do_test (void)
Packit 6c4009
{
Packit 6c4009
  /* Ensure initialization.  */
Packit 6c4009
  {
Packit 6c4009
    void *volatile ptr = malloc (1);
Packit 6c4009
    free (ptr);
Packit 6c4009
  }
Packit 6c4009
Packit 6c4009
  if (!interposed_malloc_called)
Packit 6c4009
    {
Packit 6c4009
      printf ("error: interposed malloc not called during initialization\n");
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  call_function = true;
Packit 6c4009
Packit 6c4009
  /* Bug 17702 fixes two things:
Packit 6c4009
       * A recursive dlopen unmapping the ld.so.cache.
Packit 6c4009
       * An assertion that _r_debug is RT_CONSISTENT at entry to dlopen.
Packit 6c4009
     We can only test the latter. Testing the former requires modifying
Packit 6c4009
     ld.so.conf to cache the dummy libraries, then running ldconfig,
Packit 6c4009
     then run the test. If you do all of that (and glibc's test
Packit 6c4009
     infrastructure doesn't support that yet) then the test will
Packit 6c4009
     SEGFAULT without the fix. If you don't do that, then the test
Packit 6c4009
     will abort because of the assert described in detail below.  */
Packit 6c4009
  call_func (DSO, FUNC);
Packit 6c4009
Packit 6c4009
  call_function = false;
Packit 6c4009
Packit 6c4009
  /* The function dummy2() is called by the malloc hook. Check to
Packit 6c4009
     see that it was called. This ensures the second recursive
Packit 6c4009
     dlopen happened and we called the function in that library.
Packit 6c4009
     Before the fix you either get a SIGSEGV when accessing mmap'd
Packit 6c4009
     ld.so.cache data or an assertion failure about _r_debug not
Packit 6c4009
     beint RT_CONSISTENT.  We don't test for the SIGSEGV since it
Packit 6c4009
     would require finding moddummy1 or moddummy2 in the cache and
Packit 6c4009
     we don't have any infrastructure to test that, but the _r_debug
Packit 6c4009
     assertion triggers.  */
Packit 6c4009
  printf ("Returned result is %d\n", func_result);
Packit 6c4009
  if (func_result <= 0)
Packit 6c4009
    {
Packit 6c4009
      printf ("FAIL: Function call_func() not called.\n");
Packit 6c4009
      exit (1);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  printf ("PASS: Function call_func() called more than once.\n");
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
#define TEST_FUNCTION do_test ()
Packit 6c4009
#include "../test-skeleton.c"