1  /* Test driver for malloc interposition tests.
       2     Copyright (C) 2016-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public License as
       7     published by the Free Software Foundation; either version 2.1 of the
       8     License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; see the file COPYING.LIB.  If
      17     not, see <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <stdio.h>
      20  #include <stdlib.h>
      21  #include <string.h>
      22  #include <unistd.h>
      23  
      24  #if INTERPOSE_THREADS
      25  #include <pthread.h>
      26  #endif
      27  
      28  static int do_test (void);
      29  #define TEST_FUNCTION do_test ()
      30  #include "../test-skeleton.c"
      31  
      32  /* Fills BUFFER with a test string.  */
      33  static void
      34  line_string (int number, char *buffer, size_t length)
      35  {
      36    for (size_t i = 0; i < length - 2; ++i)
      37      buffer[i] = 'A' + ((number + i) % 26);
      38    buffer[length - 2] = '\n';
      39    buffer[length - 1] = '\0';
      40  }
      41  
      42  /* Perform the tests.  */
      43  static void *
      44  run_tests (void *closure)
      45  {
      46    char *temp_file_path;
      47    int fd = create_temp_file ("tst-malloc-interpose", &temp_file_path);
      48    if (fd < 0)
      49      _exit (1);
      50  
      51    /* Line lengths excluding the line terminator.  */
      52    static const int line_lengths[] = { 0, 45, 80, 2, 8201, 0, 17, -1 };
      53  
      54    /* Fill the test file with data.  */
      55    {
      56      FILE *fp = fdopen (fd, "w");
      57      for (int lineno = 0; line_lengths[lineno] >= 0; ++lineno)
      58        {
      59          char buffer[line_lengths[lineno] + 2];
      60          line_string (lineno, buffer, sizeof (buffer));
      61          fprintf (fp, "%s", buffer);
      62        }
      63  
      64      if (ferror (fp))
      65        {
      66          printf ("error: fprintf: %m\n");
      67          _exit (1);
      68        }
      69      if (fclose (fp) != 0)
      70        {
      71          printf ("error: fclose: %m\n");
      72          _exit (1);
      73        }
      74    }
      75  
      76    /* Read the test file.  This tests libc-internal allocation with
      77       realloc.  */
      78    {
      79      FILE *fp = fopen (temp_file_path, "r");
      80  
      81      char *actual = NULL;
      82      size_t actual_size = 0;
      83      for (int lineno = 0; ; ++lineno)
      84        {
      85          errno = 0;
      86          ssize_t result = getline (&actual, &actual_size, fp);
      87          if (result == 0)
      88            {
      89              printf ("error: invalid return value 0 from getline\n");
      90              _exit (1);
      91            }
      92          if (result < 0 && errno != 0)
      93            {
      94              printf ("error: getline: %m\n");
      95              _exit (1);
      96            }
      97          if (result < 0 && line_lengths[lineno] >= 0)
      98            {
      99              printf ("error: unexpected end of file after line %d\n", lineno);
     100              _exit (1);
     101            }
     102          if (result > 0 && line_lengths[lineno] < 0)
     103            {
     104              printf ("error: no end of file after line %d\n", lineno);
     105              _exit (1);
     106            }
     107          if (result == -1 && line_lengths[lineno] == -1)
     108            /* End of file reached as expected.  */
     109            break;
     110  
     111          if (result != line_lengths[lineno] + 1)
     112            {
     113              printf ("error: line length mismatch: expected %d, got %zd\n",
     114                      line_lengths[lineno], result);
     115              _exit (1);
     116            }
     117  
     118          char expected[line_lengths[lineno] + 2];
     119          line_string (lineno, expected, sizeof (expected));
     120          if (strcmp (actual, expected) != 0)
     121            {
     122              printf ("error: line mismatch\n");
     123              printf ("error:   expected: [[%s]]\n", expected);
     124              printf ("error:   actual:   [[%s]]\n", actual);
     125              _exit (1);
     126            }
     127        }
     128  
     129      if (fclose (fp) != 0)
     130        {
     131          printf ("error: fclose (after reading): %m\n");
     132          _exit (1);
     133        }
     134    }
     135  
     136    free (temp_file_path);
     137  
     138    /* Make sure that fork is working.  */
     139    pid_t pid = fork ();
     140    if (pid == -1)
     141      {
     142        printf ("error: fork: %m\n");
     143        _exit (1);
     144      }
     145    enum { exit_code = 55 };
     146    if (pid == 0)
     147      _exit (exit_code);
     148    int status;
     149    int ret = waitpid (pid, &status, 0);
     150    if (ret < 0)
     151      {
     152        printf ("error: waitpid: %m\n");
     153        _exit (1);
     154      }
     155    if (!WIFEXITED (status) || WEXITSTATUS (status) != exit_code)
     156      {
     157        printf ("error: unexpected exit status from child process: %d\n",
     158                status);
     159        _exit (1);
     160      }
     161  
     162    return NULL;
     163  }
     164  
     165  /* This is used to detect if malloc has not been successfully
     166     interposed.  The interposed malloc does not use brk/sbrk.  */
     167  static void *initial_brk;
     168  __attribute__ ((constructor))
     169  static void
     170  set_initial_brk (void)
     171  {
     172    initial_brk = sbrk (0);
     173  }
     174  
     175  /* Terminate the process if the break value has been changed.  */
     176  __attribute__ ((destructor))
     177  static void
     178  check_brk (void)
     179  {
     180    void *current = sbrk (0);
     181    if (current != initial_brk)
     182      {
     183        printf ("error: brk changed from %p to %p; no interposition?\n",
     184                initial_brk, current);
     185        _exit (1);
     186      }
     187  }
     188  
     189  static int
     190  do_test (void)
     191  {
     192    check_brk ();
     193  
     194  #if INTERPOSE_THREADS
     195    pthread_t thr = xpthread_create (NULL, run_tests, NULL);
     196    xpthread_join (thr);
     197  #else
     198    run_tests (NULL);
     199  #endif
     200  
     201    check_brk ();
     202  
     203    return 0;
     204  }