x86: Fix Zen3/Zen4 ERMS selection (BZ 30994)
[glibc.git] / libio / tst-vtables-common.c
blob7da1a9895b464d21f53e8efa16f58eacc51a95b5
1 /* Test for libio vtables and their validation. Common code.
2 Copyright (C) 2018-2024 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
28 streams. */
30 #include <stdbool.h>
31 #include <string.h>
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>
39 #include "libioP.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
43 function. */
44 struct shared
46 /* Expected file pointer for method calls. */
47 FILE *fp;
49 /* If true, assume that a call to _IO_init is needed to enable
50 custom vtables. */
51 bool initially_disabled;
53 /* Requested return value for the methods which have one. */
54 int return_value;
56 /* A value (usually a character) recorded by some of the methods
57 below. */
58 int value;
60 /* Likewise, for some data. */
61 char buffer[16];
62 size_t buffer_length;
64 /* Total number of method calls. */
65 unsigned int 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;
87 } *shared;
89 /* Method implementations which increment the counters in *shared. */
91 static void
92 log_method (FILE *fp, const char *name)
94 if (test_verbose > 0)
95 printf ("info: %s (%p) called\n", name, fp);
98 static void
99 method_finish (FILE *fp, int dummy)
101 log_method (fp, __func__);
102 TEST_VERIFY (fp == shared->fp);
103 ++shared->calls;
104 ++shared->calls_finish;
107 static int
108 method_overflow (FILE *fp, int ch)
110 log_method (fp, __func__);
111 TEST_VERIFY (fp == shared->fp);
112 ++shared->calls;
113 ++shared->calls_overflow;
114 shared->value = ch;
115 return shared->return_value;
118 static int
119 method_underflow (FILE *fp)
121 log_method (fp, __func__);
122 TEST_VERIFY (fp == shared->fp);
123 ++shared->calls;
124 ++shared->calls_underflow;
125 return shared->return_value;
128 static int
129 method_uflow (FILE *fp)
131 log_method (fp, __func__);
132 TEST_VERIFY (fp == shared->fp);
133 ++shared->calls;
134 ++shared->calls_uflow;
135 return shared->return_value;
138 static int
139 method_pbackfail (FILE *fp, int ch)
141 log_method (fp, __func__);
142 TEST_VERIFY (fp == shared->fp);
143 ++shared->calls;
144 ++shared->calls_pbackfail;
145 shared->value = ch;
146 return shared->return_value;
149 static size_t
150 method_xsputn (FILE *fp, const void *data, size_t n)
152 log_method (fp, __func__);
153 TEST_VERIFY (fp == shared->fp);
154 ++shared->calls;
155 ++shared->calls_xsputn;
157 size_t to_copy = n;
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;
162 return to_copy;
165 static size_t
166 method_xsgetn (FILE *fp, void *data, size_t n)
168 log_method (fp, __func__);
169 TEST_VERIFY (fp == shared->fp);
170 ++shared->calls;
171 ++shared->calls_xsgetn;
172 return 0;
175 static off64_t
176 method_seekoff (FILE *fp, off64_t offset, int dir, int mode)
178 log_method (fp, __func__);
179 TEST_VERIFY (fp == shared->fp);
180 ++shared->calls;
181 ++shared->calls_seekoff;
182 return shared->return_value;
185 static off64_t
186 method_seekpos (FILE *fp, off64_t offset, int mode)
188 log_method (fp, __func__);
189 TEST_VERIFY (fp == shared->fp);
190 ++shared->calls;
191 ++shared->calls_seekpos;
192 return shared->return_value;
195 static FILE *
196 method_setbuf (FILE *fp, char *buffer, ssize_t length)
198 log_method (fp, __func__);
199 TEST_VERIFY (fp == shared->fp);
200 ++shared->calls;
201 ++shared->calls_setbuf;
202 return fp;
205 static int
206 method_sync (FILE *fp)
208 log_method (fp, __func__);
209 TEST_VERIFY (fp == shared->fp);
210 ++shared->calls;
211 ++shared->calls_sync;
212 return shared->return_value;
215 static int
216 method_doallocate (FILE *fp)
218 log_method (fp, __func__);
219 TEST_VERIFY (fp == shared->fp);
220 ++shared->calls;
221 ++shared->calls_doallocate;
222 return shared->return_value;
225 static ssize_t
226 method_read (FILE *fp, void *data, ssize_t length)
228 log_method (fp, __func__);
229 TEST_VERIFY (fp == shared->fp);
230 ++shared->calls;
231 ++shared->calls_read;
232 return shared->return_value;
235 static ssize_t
236 method_write (FILE *fp, const void *data, ssize_t length)
238 log_method (fp, __func__);
239 TEST_VERIFY (fp == shared->fp);
240 ++shared->calls;
241 ++shared->calls_write;
242 return shared->return_value;
245 static off64_t
246 method_seek (FILE *fp, off64_t offset, int mode)
248 log_method (fp, __func__);
249 TEST_VERIFY (fp == shared->fp);
250 ++shared->calls;
251 ++shared->calls_seek;
252 return shared->return_value;
255 static int
256 method_close (FILE *fp)
258 log_method (fp, __func__);
259 TEST_VERIFY (fp == shared->fp);
260 ++shared->calls;
261 ++shared->calls_close;
262 return shared->return_value;
265 static int
266 method_stat (FILE *fp, void *buffer)
268 log_method (fp, __func__);
269 TEST_VERIFY (fp == shared->fp);
270 ++shared->calls;
271 ++shared->calls_stat;
272 return shared->return_value;
275 static int
276 method_showmanyc (FILE *fp)
278 log_method (fp, __func__);
279 TEST_VERIFY (fp == shared->fp);
280 ++shared->calls;
281 ++shared->calls_showmanyc;
282 return shared->return_value;
285 static void
286 method_imbue (FILE *fp, void *locale)
288 log_method (fp, __func__);
289 TEST_VERIFY (fp == shared->fp);
290 ++shared->calls;
291 ++shared->calls_imbue;
294 /* Our custom vtable. */
296 static const struct _IO_jump_t jumps =
298 JUMP_INIT_DUMMY,
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. */
322 struct my_file
324 FILE f;
325 const struct _IO_jump_t *vtable;
328 struct my_file
329 my_file_create (void)
331 return (struct my_file)
333 /* Disable locking, so that we do not have to set up a lock
334 pointer. */
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,
341 .vtable = &jumps,
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;
351 static void
352 without_compatibility_fprintf (void *closure)
354 /* This call should abort. */
355 fprintf_ptr (shared->fp, " ");
356 _exit (1);
359 static void
360 without_compatibility_fputc (void *closure)
362 /* This call should abort. */
363 fputc (' ', shared->fp);
364 _exit (1);
367 static void
368 without_compatibility_fgetc (void *closure)
370 /* This call should abort. */
371 fgetc (shared->fp);
372 _exit (1);
375 static void
376 without_compatibility_fflush (void *closure)
378 /* This call should abort. */
379 fflush (shared->fp);
380 _exit (1);
383 static void
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;
389 shared->calls = 0;
390 struct support_capture_subprocess proc
391 = support_capture_subprocess (callback, NULL);
392 support_capture_subprocess_check (&proc, name, -SIGABRT,
393 sc_allow_stderr);
394 const char *message
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
406 verification). */
407 void _IO_init (FILE *fp, int flags);
409 static void
410 with_compatibility_fprintf (void *closure)
412 /* A temporary staging buffer is used in the current fprintf
413 implementation, which is why there is just one call to
414 xsputn. */
415 TEST_COMPARE (fprintf_ptr (shared->fp, "A%sCD", "B"), 4);
416 TEST_COMPARE (shared->calls, 1);
417 TEST_COMPARE (shared->calls_xsputn, 1);
418 TEST_COMPARE_BLOB (shared->buffer, shared->buffer_length,
419 "ABCD", 4);
422 static void
423 with_compatibility_fputc (void *closure)
425 shared->return_value = '@';
426 TEST_COMPARE (fputc ('@', shared->fp), '@');
427 TEST_COMPARE (shared->calls, 1);
428 TEST_COMPARE (shared->calls_overflow, 1);
429 TEST_COMPARE (shared->value, '@');
432 static void
433 with_compatibility_fgetc (void *closure)
435 shared->return_value = 'X';
436 TEST_COMPARE (fgetc (shared->fp), 'X');
437 TEST_COMPARE (shared->calls, 1);
438 TEST_COMPARE (shared->calls_uflow, 1);
441 static void
442 with_compatibility_fflush (void *closure)
444 TEST_COMPARE (fflush (shared->fp), 0);
445 TEST_COMPARE (shared->calls, 1);
446 TEST_COMPARE (shared->calls_sync, 1);
449 /* Call CALLBACK in a subprocess, after setting up a custom file
450 object and updating shared->fp. */
451 static void
452 check_call (const char *name, void (*callback) (void *),
453 bool initially_disabled)
455 *shared = (struct shared)
457 .initially_disabled = initially_disabled,
460 /* Set up a custom file object. */
461 struct my_file file = my_file_create ();
462 shared->fp = &file.f;
463 if (shared->initially_disabled)
464 _IO_init (shared->fp, file.f._flags);
466 if (test_verbose > 0)
467 printf ("info: calling test %s\n", name);
468 support_isolate_in_subprocess (callback, NULL);
471 /* Run the tests. INITIALLY_DISABLED indicates whether custom vtables
472 are disabled when the test starts. */
473 static int
474 run_tests (bool initially_disabled)
476 /* The test relies on fatal error messages being printed to standard
477 error. */
478 setenv ("LIBC_FATAL_STDERR_", "1", 1);
480 shared = support_shared_allocate (sizeof (*shared));
481 shared->initially_disabled = initially_disabled;
483 if (initially_disabled)
485 check_for_termination ("fprintf", without_compatibility_fprintf);
486 check_for_termination ("fputc", without_compatibility_fputc);
487 check_for_termination ("fgetc", without_compatibility_fgetc);
488 check_for_termination ("fflush", without_compatibility_fflush);
491 check_call ("fprintf", with_compatibility_fprintf, initially_disabled);
492 check_call ("fputc", with_compatibility_fputc, initially_disabled);
493 check_call ("fgetc", with_compatibility_fgetc, initially_disabled);
494 check_call ("fflush", with_compatibility_fflush, initially_disabled);
496 support_shared_free (shared);
497 shared = NULL;
499 return 0;