1 /* Test for libio vtables and their validation. Common code.
2 Copyright (C) 2018-2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
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
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
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.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
19 /* This test provides some coverage for how various stdio functions
20 use the vtables in FILE * objects. The focus is mostly on which
21 functions call which methods, not so much on validating data
22 processing. An initial series of tests check that custom vtables
23 do not work without activation through _IO_init.
25 Note: libio vtables are deprecated feature. Do not use this test
26 as a documentation source for writing custom vtables. See
27 fopencookie for a different way of creating custom stdio
32 #include <support/capture_subprocess.h>
33 #include <support/check.h>
34 #include <support/namespace.h>
35 #include <support/support.h>
36 #include <support/test-driver.h>
37 #include <support/xunistd.h>
41 /* Data shared between the test subprocess and the test driver in the
42 parent. Note that *shared is reset at the start of the check_call
46 /* Expected file pointer for method calls. */
49 /* If true, assume that a call to _IO_init is needed to enable
51 bool initially_disabled
;
53 /* Requested return value for the methods which have one. */
56 /* A value (usually a character) recorded by some of the methods
60 /* Likewise, for some data. */
64 /* Total number of method calls. */
67 /* Individual method call counts. */
68 unsigned int calls_finish
;
69 unsigned int calls_overflow
;
70 unsigned int calls_underflow
;
71 unsigned int calls_uflow
;
72 unsigned int calls_pbackfail
;
73 unsigned int calls_xsputn
;
74 unsigned int calls_xsgetn
;
75 unsigned int calls_seekoff
;
76 unsigned int calls_seekpos
;
77 unsigned int calls_setbuf
;
78 unsigned int calls_sync
;
79 unsigned int calls_doallocate
;
80 unsigned int calls_read
;
81 unsigned int calls_write
;
82 unsigned int calls_seek
;
83 unsigned int calls_close
;
84 unsigned int calls_stat
;
85 unsigned int calls_showmanyc
;
86 unsigned int calls_imbue
;
89 /* Method implementations which increment the counters in *shared. */
92 log_method (FILE *fp
, const char *name
)
95 printf ("info: %s (%p) called\n", name
, fp
);
99 method_finish (FILE *fp
, int dummy
)
101 log_method (fp
, __func__
);
102 TEST_VERIFY (fp
== shared
->fp
);
104 ++shared
->calls_finish
;
108 method_overflow (FILE *fp
, int ch
)
110 log_method (fp
, __func__
);
111 TEST_VERIFY (fp
== shared
->fp
);
113 ++shared
->calls_overflow
;
115 return shared
->return_value
;
119 method_underflow (FILE *fp
)
121 log_method (fp
, __func__
);
122 TEST_VERIFY (fp
== shared
->fp
);
124 ++shared
->calls_underflow
;
125 return shared
->return_value
;
129 method_uflow (FILE *fp
)
131 log_method (fp
, __func__
);
132 TEST_VERIFY (fp
== shared
->fp
);
134 ++shared
->calls_uflow
;
135 return shared
->return_value
;
139 method_pbackfail (FILE *fp
, int ch
)
141 log_method (fp
, __func__
);
142 TEST_VERIFY (fp
== shared
->fp
);
144 ++shared
->calls_pbackfail
;
146 return shared
->return_value
;
150 method_xsputn (FILE *fp
, const void *data
, size_t n
)
152 log_method (fp
, __func__
);
153 TEST_VERIFY (fp
== shared
->fp
);
155 ++shared
->calls_xsputn
;
158 if (n
> sizeof (shared
->buffer
))
159 to_copy
= sizeof (shared
->buffer
);
160 memcpy (shared
->buffer
, data
, to_copy
);
161 shared
->buffer_length
= to_copy
;
166 method_xsgetn (FILE *fp
, void *data
, size_t n
)
168 log_method (fp
, __func__
);
169 TEST_VERIFY (fp
== shared
->fp
);
171 ++shared
->calls_xsgetn
;
176 method_seekoff (FILE *fp
, off64_t offset
, int dir
, int mode
)
178 log_method (fp
, __func__
);
179 TEST_VERIFY (fp
== shared
->fp
);
181 ++shared
->calls_seekoff
;
182 return shared
->return_value
;
186 method_seekpos (FILE *fp
, off64_t offset
, int mode
)
188 log_method (fp
, __func__
);
189 TEST_VERIFY (fp
== shared
->fp
);
191 ++shared
->calls_seekpos
;
192 return shared
->return_value
;
196 method_setbuf (FILE *fp
, char *buffer
, ssize_t length
)
198 log_method (fp
, __func__
);
199 TEST_VERIFY (fp
== shared
->fp
);
201 ++shared
->calls_setbuf
;
206 method_sync (FILE *fp
)
208 log_method (fp
, __func__
);
209 TEST_VERIFY (fp
== shared
->fp
);
211 ++shared
->calls_sync
;
212 return shared
->return_value
;
216 method_doallocate (FILE *fp
)
218 log_method (fp
, __func__
);
219 TEST_VERIFY (fp
== shared
->fp
);
221 ++shared
->calls_doallocate
;
222 return shared
->return_value
;
226 method_read (FILE *fp
, void *data
, ssize_t length
)
228 log_method (fp
, __func__
);
229 TEST_VERIFY (fp
== shared
->fp
);
231 ++shared
->calls_read
;
232 return shared
->return_value
;
236 method_write (FILE *fp
, const void *data
, ssize_t length
)
238 log_method (fp
, __func__
);
239 TEST_VERIFY (fp
== shared
->fp
);
241 ++shared
->calls_write
;
242 return shared
->return_value
;
246 method_seek (FILE *fp
, off64_t offset
, int mode
)
248 log_method (fp
, __func__
);
249 TEST_VERIFY (fp
== shared
->fp
);
251 ++shared
->calls_seek
;
252 return shared
->return_value
;
256 method_close (FILE *fp
)
258 log_method (fp
, __func__
);
259 TEST_VERIFY (fp
== shared
->fp
);
261 ++shared
->calls_close
;
262 return shared
->return_value
;
266 method_stat (FILE *fp
, void *buffer
)
268 log_method (fp
, __func__
);
269 TEST_VERIFY (fp
== shared
->fp
);
271 ++shared
->calls_stat
;
272 return shared
->return_value
;
276 method_showmanyc (FILE *fp
)
278 log_method (fp
, __func__
);
279 TEST_VERIFY (fp
== shared
->fp
);
281 ++shared
->calls_showmanyc
;
282 return shared
->return_value
;
286 method_imbue (FILE *fp
, void *locale
)
288 log_method (fp
, __func__
);
289 TEST_VERIFY (fp
== shared
->fp
);
291 ++shared
->calls_imbue
;
294 /* Our custom vtable. */
296 static const struct _IO_jump_t jumps
=
299 JUMP_INIT (finish
, method_finish
),
300 JUMP_INIT (overflow
, method_overflow
),
301 JUMP_INIT (underflow
, method_underflow
),
302 JUMP_INIT (uflow
, method_uflow
),
303 JUMP_INIT (pbackfail
, method_pbackfail
),
304 JUMP_INIT (xsputn
, method_xsputn
),
305 JUMP_INIT (xsgetn
, method_xsgetn
),
306 JUMP_INIT (seekoff
, method_seekoff
),
307 JUMP_INIT (seekpos
, method_seekpos
),
308 JUMP_INIT (setbuf
, method_setbuf
),
309 JUMP_INIT (sync
, method_sync
),
310 JUMP_INIT (doallocate
, method_doallocate
),
311 JUMP_INIT (read
, method_read
),
312 JUMP_INIT (write
, method_write
),
313 JUMP_INIT (seek
, method_seek
),
314 JUMP_INIT (close
, method_close
),
315 JUMP_INIT (stat
, method_stat
),
316 JUMP_INIT (showmanyc
, method_showmanyc
),
317 JUMP_INIT (imbue
, method_imbue
)
320 /* Our file implementation. */
325 const struct _IO_jump_t
*vtable
;
329 my_file_create (void)
331 return (struct my_file
)
333 /* Disable locking, so that we do not have to set up a lock
335 .f
._flags
= _IO_USER_LOCK
,
337 /* Copy the offset from the an initialized handle, instead of
338 figuring it out from scratch. */
339 .f
._vtable_offset
= stdin
->_vtable_offset
,
345 /* Initial tests which do not enable vtable compatibility. */
347 /* Inhibit GCC optimization of fprintf. */
348 typedef int (*fprintf_type
) (FILE *, const char *, ...);
349 static const volatile fprintf_type fprintf_ptr
= &fprintf
;
352 without_compatibility_fprintf (void *closure
)
354 /* This call should abort. */
355 fprintf_ptr (shared
->fp
, " ");
360 without_compatibility_fputc (void *closure
)
362 /* This call should abort. */
363 fputc (' ', shared
->fp
);
368 without_compatibility_fgetc (void *closure
)
370 /* This call should abort. */
376 without_compatibility_fflush (void *closure
)
378 /* This call should abort. */
384 check_for_termination (const char *name
, void (*callback
) (void *))
386 struct my_file file
= my_file_create ();
387 shared
->fp
= &file
.f
;
388 shared
->return_value
= -1;
390 struct support_capture_subprocess proc
391 = support_capture_subprocess (callback
, NULL
);
392 support_capture_subprocess_check (&proc
, name
, -SIGABRT
,
395 = "Fatal error: glibc detected an invalid stdio handle\n";
396 TEST_COMPARE_BLOB (proc
.err
.buffer
, proc
.err
.length
,
397 message
, strlen (message
));
398 TEST_COMPARE (shared
->calls
, 0);
399 support_capture_subprocess_free (&proc
);
402 /* The test with vtable validation disabled. */
404 /* This function does not have a prototype in libioP.h to prevent
405 accidental use from within the library (which would disable vtable
407 void _IO_init (FILE *fp
, int flags
);
410 with_compatibility_fprintf (void *closure
)
412 TEST_COMPARE (fprintf_ptr (shared
->fp
, "A%sCD", "B"), 4);
413 TEST_COMPARE (shared
->calls
, 3);
414 TEST_COMPARE (shared
->calls_xsputn
, 3);
415 TEST_COMPARE_BLOB (shared
->buffer
, shared
->buffer_length
,
420 with_compatibility_fputc (void *closure
)
422 shared
->return_value
= '@';
423 TEST_COMPARE (fputc ('@', shared
->fp
), '@');
424 TEST_COMPARE (shared
->calls
, 1);
425 TEST_COMPARE (shared
->calls_overflow
, 1);
426 TEST_COMPARE (shared
->value
, '@');
430 with_compatibility_fgetc (void *closure
)
432 shared
->return_value
= 'X';
433 TEST_COMPARE (fgetc (shared
->fp
), 'X');
434 TEST_COMPARE (shared
->calls
, 1);
435 TEST_COMPARE (shared
->calls_uflow
, 1);
439 with_compatibility_fflush (void *closure
)
441 TEST_COMPARE (fflush (shared
->fp
), 0);
442 TEST_COMPARE (shared
->calls
, 1);
443 TEST_COMPARE (shared
->calls_sync
, 1);
446 /* Call CALLBACK in a subprocess, after setting up a custom file
447 object and updating shared->fp. */
449 check_call (const char *name
, void (*callback
) (void *),
450 bool initially_disabled
)
452 *shared
= (struct shared
)
454 .initially_disabled
= initially_disabled
,
457 /* Set up a custom file object. */
458 struct my_file file
= my_file_create ();
459 shared
->fp
= &file
.f
;
460 if (shared
->initially_disabled
)
461 _IO_init (shared
->fp
, file
.f
._flags
);
463 if (test_verbose
> 0)
464 printf ("info: calling test %s\n", name
);
465 support_isolate_in_subprocess (callback
, NULL
);
468 /* Run the tests. INITIALLY_DISABLED indicates whether custom vtables
469 are disabled when the test starts. */
471 run_tests (bool initially_disabled
)
473 /* The test relies on fatal error messages being printed to standard
475 setenv ("LIBC_FATAL_STDERR_", "1", 1);
477 shared
= support_shared_allocate (sizeof (*shared
));
478 shared
->initially_disabled
= initially_disabled
;
480 if (initially_disabled
)
482 check_for_termination ("fprintf", without_compatibility_fprintf
);
483 check_for_termination ("fputc", without_compatibility_fputc
);
484 check_for_termination ("fgetc", without_compatibility_fgetc
);
485 check_for_termination ("fflush", without_compatibility_fflush
);
488 check_call ("fprintf", with_compatibility_fprintf
, initially_disabled
);
489 check_call ("fputc", with_compatibility_fputc
, initially_disabled
);
490 check_call ("fgetc", with_compatibility_fgetc
, initially_disabled
);
491 check_call ("fflush", with_compatibility_fflush
, initially_disabled
);
493 support_shared_free (shared
);