]>
Commit | Line | Data |
---|---|---|
18b5e737 SP |
1 | /* Make sure that the stackaddr returned by pthread_getattr_np is |
2 | reachable. | |
3 | ||
d614a753 | 4 | Copyright (C) 2012-2020 Free Software Foundation, Inc. |
18b5e737 SP |
5 | This file is part of the GNU C Library. |
6 | ||
7 | The GNU C Library is free software; you can redistribute it and/or | |
8 | modify it under the terms of the GNU Lesser General Public | |
9 | License as published by the Free Software Foundation; either | |
10 | version 2.1 of the License, or (at your option) any later version. | |
11 | ||
12 | The GNU C Library is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | Lesser General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU Lesser General Public | |
18 | License along with the GNU C Library; if not, see | |
5a82c748 | 19 | <https://www.gnu.org/licenses/>. */ |
18b5e737 SP |
20 | |
21 | #include <stdio.h> | |
22 | #include <string.h> | |
23 | #include <sys/resource.h> | |
fc56c5bb | 24 | #include <sys/param.h> |
18b5e737 SP |
25 | #include <pthread.h> |
26 | #include <alloca.h> | |
fc56c5bb SP |
27 | #include <assert.h> |
28 | #include <unistd.h> | |
29 | #include <inttypes.h> | |
30 | ||
31 | /* There is an obscure bug in the kernel due to which RLIMIT_STACK is sometimes | |
32 | returned as unlimited when it is not, which may cause this test to fail. | |
33 | There is also the other case where RLIMIT_STACK is intentionally set as | |
34 | unlimited or very high, which may result in a vma that is too large and again | |
35 | results in a test case failure. To avoid these problems, we cap the stack | |
36 | size to one less than 8M. See the following mailing list threads for more | |
37 | information about this problem: | |
a306c790 SP |
38 | <https://sourceware.org/ml/libc-alpha/2012-06/msg00599.html> |
39 | <https://sourceware.org/ml/libc-alpha/2012-06/msg00713.html>. */ | |
fc56c5bb SP |
40 | #define MAX_STACK_SIZE (8192 * 1024 - 1) |
41 | ||
42 | static size_t pagesize; | |
43 | ||
8a814e20 FW |
44 | /* Test that the page in which TARGET lies is accessible. This will |
45 | segfault if the write fails. This function has only half a page | |
46 | of thread stack left and so should not do anything and immediately | |
47 | return the address to which the stack reached. */ | |
48 | static volatile uintptr_t | |
fc56c5bb | 49 | allocate_and_test (char *target) |
18b5e737 | 50 | { |
fc56c5bb SP |
51 | volatile char *mem = (char *) &mem; |
52 | /* FIXME: mem >= target for _STACK_GROWSUP. */ | |
53 | mem = alloca ((size_t) (mem - target)); | |
54 | ||
55 | *mem = 42; | |
8a814e20 | 56 | return (uintptr_t) mem; |
18b5e737 SP |
57 | } |
58 | ||
59 | static int | |
60 | get_self_pthread_attr (const char *id, void **stackaddr, size_t *stacksize) | |
61 | { | |
62 | pthread_attr_t attr; | |
63 | int ret; | |
64 | pthread_t me = pthread_self (); | |
65 | ||
fc56c5bb | 66 | if ((ret = pthread_getattr_np (me, &attr)) < 0) |
18b5e737 SP |
67 | { |
68 | printf ("%s: pthread_getattr_np failed: %s\n", id, strerror (ret)); | |
69 | return 1; | |
70 | } | |
71 | ||
fc56c5bb | 72 | if ((ret = pthread_attr_getstack (&attr, stackaddr, stacksize)) < 0) |
18b5e737 SP |
73 | { |
74 | printf ("%s: pthread_attr_getstack returned error: %s\n", id, | |
75 | strerror (ret)); | |
76 | return 1; | |
77 | } | |
78 | ||
79 | return 0; | |
80 | } | |
81 | ||
82 | /* Verify that the stack size returned by pthread_getattr_np is usable when | |
83 | the returned value is subject to rlimit. */ | |
84 | static int | |
85 | check_stack_top (void) | |
86 | { | |
87 | struct rlimit stack_limit; | |
88 | void *stackaddr; | |
89 | size_t stacksize = 0; | |
90 | int ret; | |
fc56c5bb | 91 | uintptr_t pagemask = ~(pagesize - 1); |
18b5e737 SP |
92 | |
93 | puts ("Verifying that stack top is accessible"); | |
94 | ||
95 | ret = getrlimit (RLIMIT_STACK, &stack_limit); | |
96 | if (ret) | |
97 | { | |
98 | perror ("getrlimit failed"); | |
99 | return 1; | |
100 | } | |
101 | ||
fc56c5bb SP |
102 | printf ("current rlimit_stack is %zu\n", (size_t) stack_limit.rlim_cur); |
103 | ||
18b5e737 SP |
104 | if (get_self_pthread_attr ("check_stack_top", &stackaddr, &stacksize)) |
105 | return 1; | |
106 | ||
fc56c5bb SP |
107 | /* Reduce the rlimit to a page less that what is currently being returned |
108 | (subject to a maximum of MAX_STACK_SIZE) so that we ensure that | |
109 | pthread_getattr_np uses rlimit. The figure is intentionally unaligned so | |
110 | to verify that pthread_getattr_np returns an aligned stacksize that | |
111 | correctly fits into the rlimit. We don't bother about the case where the | |
112 | stack is limited by the vma below it and not by the rlimit because the | |
113 | stacksize returned in that case is computed from the end of that vma and is | |
114 | hence safe. */ | |
115 | stack_limit.rlim_cur = MIN (stacksize - pagesize + 1, MAX_STACK_SIZE); | |
116 | printf ("Adjusting RLIMIT_STACK to %zu\n", (size_t) stack_limit.rlim_cur); | |
117 | if ((ret = setrlimit (RLIMIT_STACK, &stack_limit)) < 0) | |
18b5e737 SP |
118 | { |
119 | perror ("setrlimit failed"); | |
120 | return 1; | |
121 | } | |
122 | ||
123 | if (get_self_pthread_attr ("check_stack_top2", &stackaddr, &stacksize)) | |
124 | return 1; | |
125 | ||
126 | printf ("Adjusted rlimit: stacksize=%zu, stackaddr=%p\n", stacksize, | |
127 | stackaddr); | |
fc56c5bb SP |
128 | |
129 | /* A lot of targets tend to write stuff on top of the user stack during | |
130 | context switches, so we cannot possibly safely go up to the very top of | |
131 | stack and test access there. It is however sufficient to simply check if | |
132 | the top page is accessible, so we target our access halfway up the top | |
133 | page. Thanks Chris Metcalf for this idea. */ | |
8a814e20 | 134 | uintptr_t mem = allocate_and_test (stackaddr + pagesize / 2); |
fc56c5bb SP |
135 | |
136 | /* Before we celebrate, make sure we actually did test the same page. */ | |
8a814e20 | 137 | if (((uintptr_t) stackaddr & pagemask) != (mem & pagemask)) |
fc56c5bb SP |
138 | { |
139 | printf ("We successfully wrote into the wrong page.\n" | |
140 | "Expected %#" PRIxPTR ", but got %#" PRIxPTR "\n", | |
8a814e20 | 141 | (uintptr_t) stackaddr & pagemask, mem & pagemask); |
fc56c5bb SP |
142 | |
143 | return 1; | |
144 | } | |
18b5e737 SP |
145 | |
146 | puts ("Stack top tests done"); | |
147 | ||
148 | return 0; | |
149 | } | |
150 | ||
151 | /* TODO: Similar check for thread stacks once the thread stack sizes are | |
152 | fixed. */ | |
153 | static int | |
154 | do_test (void) | |
155 | { | |
fc56c5bb | 156 | pagesize = sysconf (_SC_PAGESIZE); |
18b5e737 SP |
157 | return check_stack_top (); |
158 | } | |
159 | ||
160 | ||
161 | #define TEST_FUNCTION do_test () | |
162 | #include "../test-skeleton.c" |