1/* Test concurrent fork, getline, and fflush (NULL).
2 Copyright (C) 2016-2024 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 <sys/wait.h>
20#include <unistd.h>
21#include <errno.h>
22#include <stdio.h>
23#include <pthread.h>
24#include <stdbool.h>
25#include <stdlib.h>
26#include <malloc.h>
27#include <time.h>
28#include <string.h>
29#include <signal.h>
30
31#include <support/xthread.h>
32#include <support/temp_file.h>
33#include <support/test-driver.h>
34
35enum {
36 /* Number of threads which call fork. */
37 fork_thread_count = 4,
38 /* Number of threads which call getline (and, indirectly,
39 malloc). */
40 read_thread_count = 8,
41};
42
43static bool termination_requested;
44
45static void *
46fork_thread_function (void *closure)
47{
48 while (!__atomic_load_n (&termination_requested, __ATOMIC_RELAXED))
49 {
50 pid_t pid = fork ();
51 if (pid < 0)
52 {
53 printf (format: "error: fork: %m\n");
54 abort ();
55 }
56 else if (pid == 0)
57 _exit (17);
58
59 int status;
60 if (waitpid (pid: pid, stat_loc: &status, options: 0) < 0)
61 {
62 printf (format: "error: waitpid: %m\n");
63 abort ();
64 }
65 if (!WIFEXITED (status) || WEXITSTATUS (status) != 17)
66 {
67 printf (format: "error: waitpid returned invalid status: %d\n", status);
68 abort ();
69 }
70 }
71 return NULL;
72}
73
74static char *file_to_read;
75
76static void *
77read_thread_function (void *closure)
78{
79 FILE *f = fopen (file_to_read, "r");
80 if (f == NULL)
81 {
82 printf (format: "error: fopen (%s): %m\n", file_to_read);
83 abort ();
84 }
85
86 while (!__atomic_load_n (&termination_requested, __ATOMIC_RELAXED))
87 {
88 rewind (f);
89 char *line = NULL;
90 size_t line_allocated = 0;
91 ssize_t ret = getline (lineptr: &line, n: &line_allocated, stream: f);
92 if (ret < 0)
93 {
94 printf (format: "error: getline: %m\n");
95 abort ();
96 }
97 free (ptr: line);
98 }
99 fclose (f);
100
101 return NULL;
102}
103
104static void *
105flushall_thread_function (void *closure)
106{
107 while (!__atomic_load_n (&termination_requested, __ATOMIC_RELAXED))
108 if (fflush (NULL) != 0)
109 {
110 printf (format: "error: fflush (NULL): %m\n");
111 abort ();
112 }
113 return NULL;
114}
115
116static void
117create_threads (pthread_t *threads, size_t count, void *(*func) (void *))
118{
119 for (size_t i = 0; i < count; ++i)
120 threads[i] = xpthread_create (NULL, thread_func: func, NULL);
121}
122
123static void
124join_threads (pthread_t *threads, size_t count)
125{
126 for (size_t i = 0; i < count; ++i)
127 xpthread_join (thr: threads[i]);
128}
129
130/* Create a file which consists of a single long line, and assigns
131 file_to_read. The hope is that this triggers an allocation in
132 getline which needs a lock. */
133static void
134create_file_with_large_line (void)
135{
136 int fd = create_temp_file (base: "bug19431-large-line", filename: &file_to_read);
137 if (fd < 0)
138 {
139 printf (format: "error: create_temp_file: %m\n");
140 abort ();
141 }
142 FILE *f = fdopen (fd, "w+");
143 if (f == NULL)
144 {
145 printf (format: "error: fdopen: %m\n");
146 abort ();
147 }
148 for (int i = 0; i < 50000; ++i)
149 fputc (c: 'x', stream: f);
150 fputc (c: '\n', stream: f);
151 if (ferror (stream: f))
152 {
153 printf (format: "error: fputc: %m\n");
154 abort ();
155 }
156 if (fclose (f) != 0)
157 {
158 printf (format: "error: fclose: %m\n");
159 abort ();
160 }
161}
162
163static int
164do_test (void)
165{
166 /* Make sure that we do not exceed the arena limit with the number
167 of threads we configured. */
168 if (mallopt (M_ARENA_MAX, val: 400) == 0)
169 {
170 printf (format: "error: mallopt (M_ARENA_MAX) failed\n");
171 return 1;
172 }
173
174 /* Leave some room for shutting down all threads gracefully. */
175 int timeout = 3;
176 if (timeout > DEFAULT_TIMEOUT)
177 timeout = DEFAULT_TIMEOUT - 1;
178
179 create_file_with_large_line ();
180
181 pthread_t fork_threads[fork_thread_count];
182 create_threads (threads: fork_threads, count: fork_thread_count, func: fork_thread_function);
183 pthread_t read_threads[read_thread_count];
184 create_threads (threads: read_threads, count: read_thread_count, func: read_thread_function);
185 pthread_t flushall_threads[1];
186 create_threads (threads: flushall_threads, count: 1, func: flushall_thread_function);
187
188 struct timespec ts = {timeout, 0};
189 if (nanosleep (requested_time: &ts, NULL))
190 {
191 printf (format: "error: error: nanosleep: %m\n");
192 abort ();
193 }
194
195 __atomic_store_n (&termination_requested, true, __ATOMIC_RELAXED);
196
197 join_threads (threads: flushall_threads, count: 1);
198 join_threads (threads: read_threads, count: read_thread_count);
199 join_threads (threads: fork_threads, count: fork_thread_count);
200
201 free (ptr: file_to_read);
202
203 return 0;
204}
205
206#include <support/test-driver.c>
207

source code of glibc/malloc/tst-malloc-fork-deadlock.c