1/* Copyright (C) 2005-2022 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, see
16 <https://www.gnu.org/licenses/>. */
17
18#include <errno.h>
19#include <pthread.h>
20#include <stdbool.h>
21#include <stddef.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <sys/wait.h>
26#include <stackguard-macros.h>
27#include <tls.h>
28#include <unistd.h>
29
30static const char *command;
31static bool child;
32static uintptr_t stack_chk_guard_copy;
33static bool stack_chk_guard_copy_set;
34static int fds[2];
35
36static void __attribute__ ((constructor))
37con (void)
38{
39 stack_chk_guard_copy = STACK_CHK_GUARD;
40 stack_chk_guard_copy_set = true;
41}
42
43static int
44uintptr_t_cmp (const void *a, const void *b)
45{
46 if (*(uintptr_t *) a < *(uintptr_t *) b)
47 return 1;
48 if (*(uintptr_t *) a > *(uintptr_t *) b)
49 return -1;
50 return 0;
51}
52
53static void *
54tf (void *arg)
55{
56 if (stack_chk_guard_copy != STACK_CHK_GUARD)
57 {
58 puts (s: "STACK_CHK_GUARD changed in thread");
59 return (void *) 1L;
60 }
61 return NULL;
62}
63
64static int
65do_test (void)
66{
67 if (!stack_chk_guard_copy_set)
68 {
69 puts (s: "constructor has not been run");
70 return 1;
71 }
72
73 if (stack_chk_guard_copy != STACK_CHK_GUARD)
74 {
75 puts (s: "STACK_CHK_GUARD changed between constructor and do_test");
76 return 1;
77 }
78
79 if (child)
80 {
81 int i;
82 pthread_t th[4];
83 void *ret;
84 for (i = 0; i < 4; ++i)
85 if (pthread_create (newthread: &th[i], NULL, start_routine: tf, NULL))
86 {
87 puts (s: "thread creation failed");
88 return 1;
89 }
90 for (i = 0; i < 4; ++i)
91 if (pthread_join (th: th[i], thread_return: &ret))
92 {
93 puts (s: "thread join failed");
94 return 1;
95 }
96 else if (ret != NULL)
97 return 1;
98
99 write (2, &stack_chk_guard_copy, sizeof (stack_chk_guard_copy));
100 return 0;
101 }
102
103 if (command == NULL)
104 {
105 puts (s: "missing --command or --child argument");
106 return 1;
107 }
108
109#define N 16
110 uintptr_t child_stack_chk_guards[N + 1];
111 child_stack_chk_guards[N] = stack_chk_guard_copy;
112 int i;
113 for (i = 0; i < N; ++i)
114 {
115 if (pipe (pipedes: fds) < 0)
116 {
117 printf (format: "couldn't create pipe: %m\n");
118 return 1;
119 }
120
121 pid_t pid = fork ();
122 if (pid < 0)
123 {
124 printf (format: "fork failed: %m\n");
125 return 1;
126 }
127
128 if (!pid)
129 {
130 if (stack_chk_guard_copy != STACK_CHK_GUARD)
131 {
132 puts (s: "STACK_CHK_GUARD changed after fork");
133 exit (1);
134 }
135
136 close (fd: fds[0]);
137 close (fd: 2);
138 dup2 (fd: fds[1], fd2: 2);
139 close (fd: fds[1]);
140
141 system (command: command);
142 exit (0);
143 }
144
145 close (fd: fds[1]);
146
147 if (TEMP_FAILURE_RETRY (read (fds[0], &child_stack_chk_guards[i],
148 sizeof (uintptr_t))) != sizeof (uintptr_t))
149 {
150 puts (s: "could not read stack_chk_guard value from child");
151 return 1;
152 }
153
154 close (fd: fds[0]);
155
156 pid_t termpid;
157 int status;
158 termpid = TEMP_FAILURE_RETRY (waitpid (pid, &status, 0));
159 if (termpid == -1)
160 {
161 printf (format: "waitpid failed: %m\n");
162 return 1;
163 }
164 else if (termpid != pid)
165 {
166 printf (format: "waitpid returned %ld != %ld\n",
167 (long int) termpid, (long int) pid);
168 return 1;
169 }
170 else if (!WIFEXITED (status) || WEXITSTATUS (status))
171 {
172 puts (s: "child hasn't exited with exit status 0");
173 return 1;
174 }
175 }
176
177 qsort (child_stack_chk_guards, N + 1, sizeof (uintptr_t), uintptr_t_cmp);
178
179 uintptr_t default_guard = 0;
180 unsigned char *p = (unsigned char *) &default_guard;
181 p[sizeof (uintptr_t) - 1] = 255;
182 p[sizeof (uintptr_t) - 2] = '\n';
183 p[0] = 0;
184
185 /* Test if the stack guard canaries are either randomized,
186 or equal to the default stack guard canary value.
187 Even with randomized stack guards it might happen
188 that the random number generator generates the same
189 values, but if that happens in more than half from
190 the 16 runs, something is very wrong. */
191 int ndifferences = 0;
192 int ndefaults = 0;
193 for (i = 0; i < N; ++i)
194 {
195 if (child_stack_chk_guards[i] != child_stack_chk_guards[i+1])
196 ndifferences++;
197 else if (child_stack_chk_guards[i] == default_guard)
198 ndefaults++;
199 }
200
201 printf (format: "differences %d defaults %d\n", ndifferences, ndefaults);
202
203 if (ndifferences < N / 2 && ndefaults < N / 2)
204 {
205 puts (s: "stack guard canaries are not randomized enough");
206 puts (s: "nor equal to the default canary value");
207 return 1;
208 }
209
210 return 0;
211}
212
213#define OPT_COMMAND 10000
214#define OPT_CHILD 10001
215#define CMDLINE_OPTIONS \
216 { "command", required_argument, NULL, OPT_COMMAND }, \
217 { "child", no_argument, NULL, OPT_CHILD },
218#define CMDLINE_PROCESS \
219 case OPT_COMMAND: \
220 command = optarg; \
221 break; \
222 case OPT_CHILD: \
223 child = true; \
224 break;
225#define TEST_FUNCTION do_test ()
226#include "../test-skeleton.c"
227

source code of glibc/nptl/tst-stackguard1.c