1 /* Copyright (C) 2013-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 <stdbool.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <sys/wait.h>
24 #include <stackguard-macros.h>
25 #include <tls.h>
26 #include <unistd.h>
27 
28 #ifndef _GNU_SOURCE
29 #define _GNU_SOURCE
30 #endif
31 /* Requires _GNU_SOURCE  */
32 #include <getopt.h>
33 
34 #ifndef POINTER_CHK_GUARD
35 extern uintptr_t __pointer_chk_guard;
36 # define POINTER_CHK_GUARD __pointer_chk_guard
37 #endif
38 
39 static const char *command;
40 static bool child;
41 static uintptr_t ptr_chk_guard_copy;
42 static bool ptr_chk_guard_copy_set;
43 static int fds[2];
44 
45 static void __attribute__ ((constructor))
con(void)46 con (void)
47 {
48   ptr_chk_guard_copy = POINTER_CHK_GUARD;
49   ptr_chk_guard_copy_set = true;
50 }
51 
52 static int
uintptr_t_cmp(const void * a,const void * b)53 uintptr_t_cmp (const void *a, const void *b)
54 {
55   if (*(uintptr_t *) a < *(uintptr_t *) b)
56     return 1;
57   if (*(uintptr_t *) a > *(uintptr_t *) b)
58     return -1;
59   return 0;
60 }
61 
62 static int
do_test(void)63 do_test (void)
64 {
65   if (!ptr_chk_guard_copy_set)
66     {
67       puts ("constructor has not been run");
68       return 1;
69     }
70 
71   if (ptr_chk_guard_copy != POINTER_CHK_GUARD)
72     {
73       puts ("POINTER_CHK_GUARD changed between constructor and do_test");
74       return 1;
75     }
76 
77   if (child)
78     {
79       write (2, &ptr_chk_guard_copy, sizeof (ptr_chk_guard_copy));
80       return 0;
81     }
82 
83   if (command == NULL)
84     {
85       puts ("missing --command or --child argument");
86       return 1;
87     }
88 
89 #define N 16
90   uintptr_t child_ptr_chk_guards[N + 1];
91   child_ptr_chk_guards[N] = ptr_chk_guard_copy;
92   int i;
93   for (i = 0; i < N; ++i)
94     {
95       if (pipe (fds) < 0)
96 	{
97 	  printf ("couldn't create pipe: %m\n");
98 	  return 1;
99 	}
100 
101       pid_t pid = fork ();
102       if (pid < 0)
103 	{
104 	  printf ("fork failed: %m\n");
105 	  return 1;
106 	}
107 
108       if (!pid)
109 	{
110 	  if (ptr_chk_guard_copy != POINTER_CHK_GUARD)
111 	    {
112 	      puts ("POINTER_CHK_GUARD changed after fork");
113 	      exit (1);
114 	    }
115 
116 	  close (fds[0]);
117 	  close (2);
118 	  dup2 (fds[1], 2);
119 	  close (fds[1]);
120 
121 	  system (command);
122 	  exit (0);
123 	}
124 
125       close (fds[1]);
126 
127       if (TEMP_FAILURE_RETRY (read (fds[0], &child_ptr_chk_guards[i],
128 				    sizeof (uintptr_t))) != sizeof (uintptr_t))
129 	{
130 	  puts ("could not read ptr_chk_guard value from child");
131 	  return 1;
132 	}
133 
134       close (fds[0]);
135 
136       pid_t termpid;
137       int status;
138       termpid = TEMP_FAILURE_RETRY (waitpid (pid, &status, 0));
139       if (termpid == -1)
140 	{
141 	  printf ("waitpid failed: %m\n");
142 	  return 1;
143 	}
144       else if (termpid != pid)
145 	{
146 	  printf ("waitpid returned %ld != %ld\n",
147 		  (long int) termpid, (long int) pid);
148 	  return 1;
149 	}
150       else if (!WIFEXITED (status) || WEXITSTATUS (status))
151 	{
152 	  puts ("child hasn't exited with exit status 0");
153 	  return 1;
154 	}
155     }
156 
157   qsort (child_ptr_chk_guards, N + 1, sizeof (uintptr_t), uintptr_t_cmp);
158 
159   /* The default pointer guard is the same as the default stack guard.
160      They are only set to default if dl_random is NULL.  */
161   uintptr_t default_guard = 0;
162   unsigned char *p = (unsigned char *) &default_guard;
163   p[sizeof (uintptr_t) - 1] = 255;
164   p[sizeof (uintptr_t) - 2] = '\n';
165   p[0] = 0;
166 
167   /* Test if the pointer guard canaries are either randomized,
168      or equal to the default pointer guard value.
169      Even with randomized pointer guards it might happen
170      that the random number generator generates the same
171      values, but if that happens in more than half from
172      the 16 runs, something is very wrong.  */
173   int ndifferences = 0;
174   int ndefaults = 0;
175   for (i = 0; i < N; ++i)
176     {
177       if (child_ptr_chk_guards[i] != child_ptr_chk_guards[i+1])
178 	ndifferences++;
179       else if (child_ptr_chk_guards[i] == default_guard)
180 	ndefaults++;
181     }
182 
183   printf ("differences %d defaults %d\n", ndifferences, ndefaults);
184 
185   if (ndifferences < N / 2 && ndefaults < N / 2)
186     {
187       puts ("pointer guard values are not randomized enough");
188       puts ("nor equal to the default value");
189       return 1;
190     }
191 
192   return 0;
193 }
194 
195 #define OPT_COMMAND	10000
196 #define OPT_CHILD	10001
197 #define CMDLINE_OPTIONS	\
198   { "command", required_argument, NULL, OPT_COMMAND },  \
199   { "child", no_argument, NULL, OPT_CHILD },
200 
201 static void __attribute((used))
cmdline_process_function(int c)202 cmdline_process_function (int c)
203 {
204   switch (c)
205     {
206       case OPT_COMMAND:
207         command = optarg;
208         break;
209       case OPT_CHILD:
210         child = true;
211         break;
212     }
213 }
214 
215 #define CMDLINE_PROCESS	cmdline_process_function
216 
217 #include <support/test-driver.c>
218