1 | /* Check the VMA name decoration. |
2 | Copyright (C) 2023-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 |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the 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; if not, see |
17 | <https://www.gnu.org/licenses/>. */ |
18 | |
19 | #include <stdlib.h> |
20 | #include <string.h> |
21 | #include <support/check.h> |
22 | #include <support/support.h> |
23 | #include <support/test-driver.h> |
24 | #include <support/xstdio.h> |
25 | #include <support/xthread.h> |
26 | #include <support/xunistd.h> |
27 | #include <sys/mman.h> |
28 | |
29 | #ifndef MAP_STACK |
30 | # define MAP_STACK 0 |
31 | #endif |
32 | |
33 | static pthread_barrier_t b; |
34 | |
35 | static int expected_n_arenas; |
36 | |
37 | static void * |
38 | tf (void *closure) |
39 | { |
40 | void *p = xmalloc (n: 1024); |
41 | |
42 | /* Wait the thread startup, so thread stack is allocated. */ |
43 | xpthread_barrier_wait (barrier: &b); |
44 | |
45 | /* Wait the test to read the process mapping. */ |
46 | xpthread_barrier_wait (barrier: &b); |
47 | |
48 | free (ptr: p); |
49 | |
50 | return NULL; |
51 | } |
52 | |
53 | struct proc_maps_t |
54 | { |
55 | int n_def_threads; |
56 | int n_user_threads; |
57 | int n_arenas; |
58 | int n_malloc_mmap; |
59 | int n_loader_malloc_mmap; |
60 | }; |
61 | |
62 | static struct proc_maps_t |
63 | read_proc_maps (void) |
64 | { |
65 | if (test_verbose) |
66 | printf (format: "=== print process %jd memory mapping ===\n" , |
67 | (intmax_t) getpid ()); |
68 | struct proc_maps_t r = { 0 }; |
69 | |
70 | FILE *f = xfopen (path: "/proc/self/maps" , mode: "r" ); |
71 | char *line = NULL; |
72 | size_t line_len = 0; |
73 | while (xgetline (lineptr: &line, n: &line_len, stream: f)) |
74 | { |
75 | if (test_verbose) |
76 | printf (format: "%s" , line); |
77 | if (strstr (haystack: line, needle: "[anon: glibc: pthread stack:" ) != NULL) |
78 | r.n_def_threads++; |
79 | else if (strstr (haystack: line, needle: "[anon: glibc: pthread user stack:" ) != NULL) |
80 | r.n_user_threads++; |
81 | else if (strstr (haystack: line, needle: "[anon: glibc: malloc arena]" ) != NULL) |
82 | r.n_arenas++; |
83 | else if (strstr (haystack: line, needle: "[anon: glibc: malloc]" ) != NULL) |
84 | r.n_malloc_mmap++; |
85 | else if (strstr (haystack: line, needle: "[anon: glibc: loader malloc]" ) != NULL) |
86 | r.n_loader_malloc_mmap++; |
87 | } |
88 | free (ptr: line); |
89 | xfclose (f); |
90 | |
91 | if (test_verbose) |
92 | printf (format: "===\n" ); |
93 | return r; |
94 | } |
95 | |
96 | static void |
97 | do_test_threads (bool set_guard) |
98 | { |
99 | enum |
100 | { |
101 | num_def_threads = 8, |
102 | num_user_threads = 2, |
103 | num_threads = num_def_threads + num_user_threads, |
104 | }; |
105 | |
106 | xpthread_barrier_init (barrier: &b, NULL, count: num_threads + 1); |
107 | |
108 | /* Issue a large malloc to trigger a mmap call. */ |
109 | void *p = xmalloc (n: 256 * 1024); |
110 | |
111 | pthread_t thr[num_threads]; |
112 | { |
113 | int i = 0; |
114 | for (; i < num_threads - num_user_threads; i++) |
115 | { |
116 | pthread_attr_t attr; |
117 | xpthread_attr_init (attr: &attr); |
118 | /* The guard page is not annotated. */ |
119 | if (!set_guard) |
120 | xpthread_attr_setguardsize (attr: &attr, guardsize: 0); |
121 | thr[i] = xpthread_create (attr: &attr, thread_func: tf, NULL); |
122 | xpthread_attr_destroy (attr: &attr); |
123 | } |
124 | for (; i < num_threads; i++) |
125 | { |
126 | pthread_attr_t attr; |
127 | xpthread_attr_init (attr: &attr); |
128 | size_t stacksize = support_small_thread_stack_size (); |
129 | void *stack = xmmap (addr: 0, |
130 | length: stacksize, |
131 | PROT_READ | PROT_WRITE, |
132 | MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, |
133 | fd: -1); |
134 | xpthread_attr_setstack (attr: &attr, stackaddr: stack, stacksize); |
135 | if (!set_guard) |
136 | xpthread_attr_setguardsize (attr: &attr, guardsize: 0); |
137 | thr[i] = xpthread_create (attr: &attr, thread_func: tf, NULL); |
138 | xpthread_attr_destroy (attr: &attr); |
139 | } |
140 | } |
141 | |
142 | /* Wait all threads to finshed statup and stack allocation. */ |
143 | xpthread_barrier_wait (barrier: &b); |
144 | |
145 | { |
146 | struct proc_maps_t r = read_proc_maps (); |
147 | TEST_COMPARE (r.n_def_threads, num_def_threads); |
148 | TEST_COMPARE (r.n_user_threads, num_user_threads); |
149 | TEST_COMPARE (r.n_arenas, expected_n_arenas); |
150 | TEST_COMPARE (r.n_malloc_mmap, 1); |
151 | /* On some architectures the loader might use more than one page. */ |
152 | TEST_VERIFY (r.n_loader_malloc_mmap >= 1); |
153 | } |
154 | |
155 | /* Let the threads finish. */ |
156 | xpthread_barrier_wait (barrier: &b); |
157 | |
158 | for (int i = 0; i < num_threads; i++) |
159 | xpthread_join (thr: thr[i]); |
160 | |
161 | { |
162 | struct proc_maps_t r = read_proc_maps (); |
163 | TEST_COMPARE (r.n_def_threads, 0); |
164 | TEST_COMPARE (r.n_user_threads, 0); |
165 | TEST_COMPARE (r.n_arenas, expected_n_arenas); |
166 | TEST_COMPARE (r.n_malloc_mmap, 1); |
167 | TEST_VERIFY (r.n_loader_malloc_mmap >= 1); |
168 | } |
169 | |
170 | free (ptr: p); |
171 | } |
172 | |
173 | static void |
174 | do_prepare (int argc, char *argv[]) |
175 | { |
176 | TEST_VERIFY_EXIT (argc == 2); |
177 | expected_n_arenas = strtol (argv[1], NULL, 10); |
178 | expected_n_arenas = expected_n_arenas - 1; |
179 | } |
180 | #define PREPARE do_prepare |
181 | |
182 | static int |
183 | do_test (void) |
184 | { |
185 | support_need_proc (why_msg: "Reads /proc/self/maps to get stack names." ); |
186 | |
187 | if (!support_set_vma_name_supported ()) |
188 | FAIL_UNSUPPORTED ("kernel does not support PR_SET_VMA_ANON_NAME" ); |
189 | |
190 | do_test_threads (false); |
191 | do_test_threads (true); |
192 | |
193 | return 0; |
194 | } |
195 | |
196 | #include <support/test-driver.c> |
197 | |