Blame libio/tst-vtables-common.c

Packit 6c4009
/* Test for libio vtables and their validation.  Common code.
Packit 6c4009
   Copyright (C) 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
/* This test provides some coverage for how various stdio functions
Packit 6c4009
   use the vtables in FILE * objects.  The focus is mostly on which
Packit 6c4009
   functions call which methods, not so much on validating data
Packit 6c4009
   processing.  An initial series of tests check that custom vtables
Packit 6c4009
   do not work without activation through _IO_init.
Packit 6c4009
Packit 6c4009
   Note: libio vtables are deprecated feature.  Do not use this test
Packit 6c4009
   as a documentation source for writing custom vtables.  See
Packit 6c4009
   fopencookie for a different way of creating custom stdio
Packit 6c4009
   streams.  */
Packit 6c4009
Packit 6c4009
#include <stdbool.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <support/capture_subprocess.h>
Packit 6c4009
#include <support/check.h>
Packit 6c4009
#include <support/namespace.h>
Packit 6c4009
#include <support/support.h>
Packit 6c4009
#include <support/test-driver.h>
Packit 6c4009
#include <support/xunistd.h>
Packit 6c4009
Packit 6c4009
#include "libioP.h"
Packit 6c4009
Packit 6c4009
/* Data shared between the test subprocess and the test driver in the
Packit 6c4009
   parent.  Note that *shared is reset at the start of the check_call
Packit 6c4009
   function.  */
Packit 6c4009
struct shared
Packit 6c4009
{
Packit 6c4009
  /* Expected file pointer for method calls.  */
Packit 6c4009
  FILE *fp;
Packit 6c4009
Packit 6c4009
  /* If true, assume that a call to _IO_init is needed to enable
Packit 6c4009
     custom vtables.  */
Packit 6c4009
  bool initially_disabled;
Packit 6c4009
Packit 6c4009
  /* Requested return value for the methods which have one.  */
Packit 6c4009
  int return_value;
Packit 6c4009
Packit 6c4009
  /* A value (usually a character) recorded by some of the methods
Packit 6c4009
     below.  */
Packit 6c4009
  int value;
Packit 6c4009
Packit 6c4009
  /* Likewise, for some data.  */
Packit 6c4009
  char buffer[16];
Packit 6c4009
  size_t buffer_length;
Packit 6c4009
Packit 6c4009
  /* Total number of method calls.  */
Packit 6c4009
  unsigned int calls;
Packit 6c4009
Packit 6c4009
  /* Individual method call counts.  */
Packit 6c4009
  unsigned int calls_finish;
Packit 6c4009
  unsigned int calls_overflow;
Packit 6c4009
  unsigned int calls_underflow;
Packit 6c4009
  unsigned int calls_uflow;
Packit 6c4009
  unsigned int calls_pbackfail;
Packit 6c4009
  unsigned int calls_xsputn;
Packit 6c4009
  unsigned int calls_xsgetn;
Packit 6c4009
  unsigned int calls_seekoff;
Packit 6c4009
  unsigned int calls_seekpos;
Packit 6c4009
  unsigned int calls_setbuf;
Packit 6c4009
  unsigned int calls_sync;
Packit 6c4009
  unsigned int calls_doallocate;
Packit 6c4009
  unsigned int calls_read;
Packit 6c4009
  unsigned int calls_write;
Packit 6c4009
  unsigned int calls_seek;
Packit 6c4009
  unsigned int calls_close;
Packit 6c4009
  unsigned int calls_stat;
Packit 6c4009
  unsigned int calls_showmanyc;
Packit 6c4009
  unsigned int calls_imbue;
Packit 6c4009
} *shared;
Packit 6c4009
Packit 6c4009
/* Method implementations which increment the counters in *shared.  */
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
log_method (FILE *fp, const char *name)
Packit 6c4009
{
Packit 6c4009
  if (test_verbose > 0)
Packit 6c4009
    printf ("info: %s (%p) called\n", name, fp);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
method_finish (FILE *fp, int dummy)
Packit 6c4009
{
Packit 6c4009
  log_method (fp, __func__);
Packit 6c4009
  TEST_VERIFY (fp == shared->fp);
Packit 6c4009
  ++shared->calls;
Packit 6c4009
  ++shared->calls_finish;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
method_overflow (FILE *fp, int ch)
Packit 6c4009
{
Packit 6c4009
  log_method (fp, __func__);
Packit 6c4009
  TEST_VERIFY (fp == shared->fp);
Packit 6c4009
  ++shared->calls;
Packit 6c4009
  ++shared->calls_overflow;
Packit 6c4009
  shared->value = ch;
Packit 6c4009
  return shared->return_value;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
method_underflow (FILE *fp)
Packit 6c4009
{
Packit 6c4009
  log_method (fp, __func__);
Packit 6c4009
  TEST_VERIFY (fp == shared->fp);
Packit 6c4009
  ++shared->calls;
Packit 6c4009
  ++shared->calls_underflow;
Packit 6c4009
  return shared->return_value;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
method_uflow (FILE *fp)
Packit 6c4009
{
Packit 6c4009
  log_method (fp, __func__);
Packit 6c4009
  TEST_VERIFY (fp == shared->fp);
Packit 6c4009
  ++shared->calls;
Packit 6c4009
  ++shared->calls_uflow;
Packit 6c4009
  return shared->return_value;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
method_pbackfail (FILE *fp, int ch)
Packit 6c4009
{
Packit 6c4009
  log_method (fp, __func__);
Packit 6c4009
  TEST_VERIFY (fp == shared->fp);
Packit 6c4009
  ++shared->calls;
Packit 6c4009
  ++shared->calls_pbackfail;
Packit 6c4009
  shared->value = ch;
Packit 6c4009
  return shared->return_value;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static size_t
Packit 6c4009
method_xsputn (FILE *fp, const void *data, size_t n)
Packit 6c4009
{
Packit 6c4009
  log_method (fp, __func__);
Packit 6c4009
  TEST_VERIFY (fp == shared->fp);
Packit 6c4009
  ++shared->calls;
Packit 6c4009
  ++shared->calls_xsputn;
Packit 6c4009
Packit 6c4009
  size_t to_copy = n;
Packit 6c4009
  if (n > sizeof (shared->buffer))
Packit 6c4009
    to_copy = sizeof (shared->buffer);
Packit 6c4009
  memcpy (shared->buffer, data, to_copy);
Packit 6c4009
  shared->buffer_length = to_copy;
Packit 6c4009
  return to_copy;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static size_t
Packit 6c4009
method_xsgetn (FILE *fp, void *data, size_t n)
Packit 6c4009
{
Packit 6c4009
  log_method (fp, __func__);
Packit 6c4009
  TEST_VERIFY (fp == shared->fp);
Packit 6c4009
  ++shared->calls;
Packit 6c4009
  ++shared->calls_xsgetn;
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static off64_t
Packit 6c4009
method_seekoff (FILE *fp, off64_t offset, int dir, int mode)
Packit 6c4009
{
Packit 6c4009
  log_method (fp, __func__);
Packit 6c4009
  TEST_VERIFY (fp == shared->fp);
Packit 6c4009
  ++shared->calls;
Packit 6c4009
  ++shared->calls_seekoff;
Packit 6c4009
  return shared->return_value;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static off64_t
Packit 6c4009
method_seekpos (FILE *fp, off64_t offset, int mode)
Packit 6c4009
{
Packit 6c4009
  log_method (fp, __func__);
Packit 6c4009
  TEST_VERIFY (fp == shared->fp);
Packit 6c4009
  ++shared->calls;
Packit 6c4009
  ++shared->calls_seekpos;
Packit 6c4009
  return shared->return_value;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static FILE *
Packit 6c4009
method_setbuf (FILE *fp, char *buffer, ssize_t length)
Packit 6c4009
{
Packit 6c4009
  log_method (fp, __func__);
Packit 6c4009
  TEST_VERIFY (fp == shared->fp);
Packit 6c4009
  ++shared->calls;
Packit 6c4009
  ++shared->calls_setbuf;
Packit 6c4009
  return fp;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
method_sync (FILE *fp)
Packit 6c4009
{
Packit 6c4009
  log_method (fp, __func__);
Packit 6c4009
  TEST_VERIFY (fp == shared->fp);
Packit 6c4009
  ++shared->calls;
Packit 6c4009
  ++shared->calls_sync;
Packit 6c4009
  return shared->return_value;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
method_doallocate (FILE *fp)
Packit 6c4009
{
Packit 6c4009
  log_method (fp, __func__);
Packit 6c4009
  TEST_VERIFY (fp == shared->fp);
Packit 6c4009
  ++shared->calls;
Packit 6c4009
  ++shared->calls_doallocate;
Packit 6c4009
  return shared->return_value;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static ssize_t
Packit 6c4009
method_read (FILE *fp, void *data, ssize_t length)
Packit 6c4009
{
Packit 6c4009
  log_method (fp, __func__);
Packit 6c4009
  TEST_VERIFY (fp == shared->fp);
Packit 6c4009
  ++shared->calls;
Packit 6c4009
  ++shared->calls_read;
Packit 6c4009
  return shared->return_value;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static ssize_t
Packit 6c4009
method_write (FILE *fp, const void *data, ssize_t length)
Packit 6c4009
{
Packit 6c4009
  log_method (fp, __func__);
Packit 6c4009
  TEST_VERIFY (fp == shared->fp);
Packit 6c4009
  ++shared->calls;
Packit 6c4009
  ++shared->calls_write;
Packit 6c4009
  return shared->return_value;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static off64_t
Packit 6c4009
method_seek (FILE *fp, off64_t offset, int mode)
Packit 6c4009
{
Packit 6c4009
  log_method (fp, __func__);
Packit 6c4009
  TEST_VERIFY (fp == shared->fp);
Packit 6c4009
  ++shared->calls;
Packit 6c4009
  ++shared->calls_seek;
Packit 6c4009
  return shared->return_value;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
method_close (FILE *fp)
Packit 6c4009
{
Packit 6c4009
  log_method (fp, __func__);
Packit 6c4009
  TEST_VERIFY (fp == shared->fp);
Packit 6c4009
  ++shared->calls;
Packit 6c4009
  ++shared->calls_close;
Packit 6c4009
  return shared->return_value;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
method_stat (FILE *fp, void *buffer)
Packit 6c4009
{
Packit 6c4009
  log_method (fp, __func__);
Packit 6c4009
  TEST_VERIFY (fp == shared->fp);
Packit 6c4009
  ++shared->calls;
Packit 6c4009
  ++shared->calls_stat;
Packit 6c4009
  return shared->return_value;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
method_showmanyc (FILE *fp)
Packit 6c4009
{
Packit 6c4009
  log_method (fp, __func__);
Packit 6c4009
  TEST_VERIFY (fp == shared->fp);
Packit 6c4009
  ++shared->calls;
Packit 6c4009
  ++shared->calls_showmanyc;
Packit 6c4009
  return shared->return_value;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
method_imbue (FILE *fp, void *locale)
Packit 6c4009
{
Packit 6c4009
  log_method (fp, __func__);
Packit 6c4009
  TEST_VERIFY (fp == shared->fp);
Packit 6c4009
  ++shared->calls;
Packit 6c4009
  ++shared->calls_imbue;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Our custom vtable.  */
Packit 6c4009
Packit 6c4009
static const struct _IO_jump_t jumps =
Packit 6c4009
{
Packit 6c4009
  JUMP_INIT_DUMMY,
Packit 6c4009
  JUMP_INIT (finish, method_finish),
Packit 6c4009
  JUMP_INIT (overflow, method_overflow),
Packit 6c4009
  JUMP_INIT (underflow, method_underflow),
Packit 6c4009
  JUMP_INIT (uflow, method_uflow),
Packit 6c4009
  JUMP_INIT (pbackfail, method_pbackfail),
Packit 6c4009
  JUMP_INIT (xsputn, method_xsputn),
Packit 6c4009
  JUMP_INIT (xsgetn, method_xsgetn),
Packit 6c4009
  JUMP_INIT (seekoff, method_seekoff),
Packit 6c4009
  JUMP_INIT (seekpos, method_seekpos),
Packit 6c4009
  JUMP_INIT (setbuf, method_setbuf),
Packit 6c4009
  JUMP_INIT (sync, method_sync),
Packit 6c4009
  JUMP_INIT (doallocate, method_doallocate),
Packit 6c4009
  JUMP_INIT (read, method_read),
Packit 6c4009
  JUMP_INIT (write, method_write),
Packit 6c4009
  JUMP_INIT (seek, method_seek),
Packit 6c4009
  JUMP_INIT (close, method_close),
Packit 6c4009
  JUMP_INIT (stat, method_stat),
Packit 6c4009
  JUMP_INIT (showmanyc, method_showmanyc),
Packit 6c4009
  JUMP_INIT (imbue, method_imbue)
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
/* Our file implementation.  */
Packit 6c4009
Packit 6c4009
struct my_file
Packit 6c4009
{
Packit 6c4009
  FILE f;
Packit 6c4009
  const struct _IO_jump_t *vtable;
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
struct my_file
Packit 6c4009
my_file_create (void)
Packit 6c4009
{
Packit 6c4009
  return (struct my_file)
Packit 6c4009
    {
Packit 6c4009
      /* Disable locking, so that we do not have to set up a lock
Packit 6c4009
         pointer.  */
Packit 6c4009
      .f._flags =  _IO_USER_LOCK,
Packit 6c4009
Packit 6c4009
      /* Copy the offset from the an initialized handle, instead of
Packit 6c4009
         figuring it out from scratch.  */
Packit 6c4009
      .f._vtable_offset = stdin->_vtable_offset,
Packit 6c4009
Packit 6c4009
      .vtable = &jumps,
Packit 6c4009
    };
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Initial tests which do not enable vtable compatibility.  */
Packit 6c4009
Packit 6c4009
/* Inhibit GCC optimization of fprintf.  */
Packit 6c4009
typedef int (*fprintf_type) (FILE *, const char *, ...);
Packit 6c4009
static const volatile fprintf_type fprintf_ptr = &fprintf;
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
without_compatibility_fprintf (void *closure)
Packit 6c4009
{
Packit 6c4009
  /* This call should abort.  */
Packit 6c4009
  fprintf_ptr (shared->fp, " ");
Packit 6c4009
  _exit (1);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
without_compatibility_fputc (void *closure)
Packit 6c4009
{
Packit 6c4009
  /* This call should abort.  */
Packit 6c4009
  fputc (' ', shared->fp);
Packit 6c4009
  _exit (1);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
without_compatibility_fgetc (void *closure)
Packit 6c4009
{
Packit 6c4009
  /* This call should abort.  */
Packit 6c4009
  fgetc (shared->fp);
Packit 6c4009
  _exit (1);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
without_compatibility_fflush (void *closure)
Packit 6c4009
{
Packit 6c4009
  /* This call should abort.  */
Packit 6c4009
  fflush (shared->fp);
Packit 6c4009
  _exit (1);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
check_for_termination (const char *name, void (*callback) (void *))
Packit 6c4009
{
Packit 6c4009
  struct my_file file = my_file_create ();
Packit 6c4009
  shared->fp = &file.f;
Packit 6c4009
  shared->return_value = -1;
Packit 6c4009
  shared->calls = 0;
Packit 6c4009
  struct support_capture_subprocess proc
Packit 6c4009
    = support_capture_subprocess (callback, NULL);
Packit Bot 2f1cd5
  support_capture_subprocess_check (&proc, name, -SIGABRT,
Packit 6c4009
                                    sc_allow_stderr);
Packit 6c4009
  const char *message
Packit 6c4009
    = "Fatal error: glibc detected an invalid stdio handle\n";
Packit 6c4009
  TEST_COMPARE_BLOB (proc.err.buffer, proc.err.length,
Packit 6c4009
                     message, strlen (message));
Packit 6c4009
  TEST_COMPARE (shared->calls, 0);
Packit 6c4009
  support_capture_subprocess_free (&proc;;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* The test with vtable validation disabled.  */
Packit 6c4009
Packit 6c4009
/* This function does not have a prototype in libioP.h to prevent
Packit 6c4009
   accidental use from within the library (which would disable vtable
Packit 6c4009
   verification).  */
Packit 6c4009
void _IO_init (FILE *fp, int flags);
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
with_compatibility_fprintf (void *closure)
Packit 6c4009
{
Packit 6c4009
  TEST_COMPARE (fprintf_ptr (shared->fp, "A%sCD", "B"), 4);
Packit 6c4009
  TEST_COMPARE (shared->calls, 3);
Packit 6c4009
  TEST_COMPARE (shared->calls_xsputn, 3);
Packit 6c4009
  TEST_COMPARE_BLOB (shared->buffer, shared->buffer_length,
Packit 6c4009
                     "CD", 2);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
with_compatibility_fputc (void *closure)
Packit 6c4009
{
Packit 6c4009
  shared->return_value = '@';
Packit 6c4009
  TEST_COMPARE (fputc ('@', shared->fp), '@');
Packit 6c4009
  TEST_COMPARE (shared->calls, 1);
Packit 6c4009
  TEST_COMPARE (shared->calls_overflow, 1);
Packit 6c4009
  TEST_COMPARE (shared->value, '@');
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
with_compatibility_fgetc (void *closure)
Packit 6c4009
{
Packit 6c4009
  shared->return_value = 'X';
Packit 6c4009
  TEST_COMPARE (fgetc (shared->fp), 'X');
Packit 6c4009
  TEST_COMPARE (shared->calls, 1);
Packit 6c4009
  TEST_COMPARE (shared->calls_uflow, 1);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
with_compatibility_fflush (void *closure)
Packit 6c4009
{
Packit 6c4009
  TEST_COMPARE (fflush (shared->fp), 0);
Packit 6c4009
  TEST_COMPARE (shared->calls, 1);
Packit 6c4009
  TEST_COMPARE (shared->calls_sync, 1);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Call CALLBACK in a subprocess, after setting up a custom file
Packit 6c4009
   object and updating shared->fp.  */
Packit 6c4009
static void
Packit 6c4009
check_call (const char *name, void (*callback) (void *),
Packit 6c4009
            bool initially_disabled)
Packit 6c4009
{
Packit 6c4009
  *shared = (struct shared)
Packit 6c4009
    {
Packit 6c4009
     .initially_disabled = initially_disabled,
Packit 6c4009
    };
Packit 6c4009
Packit 6c4009
  /* Set up a custom file object.  */
Packit 6c4009
  struct my_file file = my_file_create ();
Packit 6c4009
  shared->fp = &file.f;
Packit 6c4009
  if (shared->initially_disabled)
Packit 6c4009
    _IO_init (shared->fp, file.f._flags);
Packit 6c4009
Packit 6c4009
  if (test_verbose > 0)
Packit 6c4009
    printf ("info: calling test %s\n", name);
Packit 6c4009
  support_isolate_in_subprocess (callback, NULL);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Run the tests.  INITIALLY_DISABLED indicates whether custom vtables
Packit 6c4009
   are disabled when the test starts.  */
Packit 6c4009
static int
Packit 6c4009
run_tests (bool initially_disabled)
Packit 6c4009
{
Packit 6c4009
  /* The test relies on fatal error messages being printed to standard
Packit 6c4009
     error.  */
Packit 6c4009
  setenv ("LIBC_FATAL_STDERR_", "1", 1);
Packit 6c4009
Packit 6c4009
  shared = support_shared_allocate (sizeof (*shared));
Packit 6c4009
  shared->initially_disabled = initially_disabled;
Packit 6c4009
Packit 6c4009
  if (initially_disabled)
Packit 6c4009
    {
Packit 6c4009
      check_for_termination ("fprintf", without_compatibility_fprintf);
Packit 6c4009
      check_for_termination ("fputc", without_compatibility_fputc);
Packit 6c4009
      check_for_termination ("fgetc", without_compatibility_fgetc);
Packit 6c4009
      check_for_termination ("fflush", without_compatibility_fflush);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  check_call ("fprintf", with_compatibility_fprintf, initially_disabled);
Packit 6c4009
  check_call ("fputc", with_compatibility_fputc, initially_disabled);
Packit 6c4009
  check_call ("fgetc", with_compatibility_fgetc, initially_disabled);
Packit 6c4009
  check_call ("fflush", with_compatibility_fflush, initially_disabled);
Packit 6c4009
Packit 6c4009
  support_shared_free (shared);
Packit 6c4009
  shared = NULL;
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}