1 /* Copyright (C) 2002-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 <assert.h>
19 #include <errno.h>
20 #include <inttypes.h>
21 #include <stdio.h>
22 #include <stdio_ext.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/resource.h>
26 #include "pthreadP.h"
27 #include <lowlevellock.h>
28 #include <ldsodefs.h>
29 
30 
31 int
__pthread_getattr_np(pthread_t thread_id,pthread_attr_t * attr)32 __pthread_getattr_np (pthread_t thread_id, pthread_attr_t *attr)
33 {
34   struct pthread *thread = (struct pthread *) thread_id;
35 
36   /* Prepare the new thread attribute.  */
37   int ret = __pthread_attr_init (attr);
38   if (ret != 0)
39     return ret;
40 
41   struct pthread_attr *iattr = (struct pthread_attr *) attr;
42 
43   lll_lock (thread->lock, LLL_PRIVATE);
44 
45   /* The thread library is responsible for keeping the values in the
46      thread desriptor up-to-date in case the user changes them.  */
47   memcpy (&iattr->schedparam, &thread->schedparam,
48 	  sizeof (struct sched_param));
49   iattr->schedpolicy = thread->schedpolicy;
50 
51   /* Clear the flags work.  */
52   iattr->flags = thread->flags;
53 
54   /* The thread might be detached by now.  */
55   if (IS_DETACHED (thread))
56     iattr->flags |= ATTR_FLAG_DETACHSTATE;
57 
58   /* This is the guardsize after adjusting it.  */
59   iattr->guardsize = thread->reported_guardsize;
60 
61   /* The sizes are subject to alignment.  */
62   if (__glibc_likely (thread->stackblock != NULL))
63     {
64       /* The stack size reported to the user should not include the
65 	 guard size.  */
66       iattr->stacksize = thread->stackblock_size - thread->guardsize;
67 #if _STACK_GROWS_DOWN
68       iattr->stackaddr = (char *) thread->stackblock
69 			 + thread->stackblock_size;
70 #else
71       iattr->stackaddr = (char *) thread->stackblock;
72 #endif
73     }
74   else
75     {
76       /* No stack information available.  This must be for the initial
77 	 thread.  Get the info in some magical way.  */
78 
79       /* Stack size limit.  */
80       struct rlimit rl;
81 
82       /* The safest way to get the top of the stack is to read
83 	 /proc/self/maps and locate the line into which
84 	 __libc_stack_end falls.  */
85       FILE *fp = fopen ("/proc/self/maps", "rce");
86       if (fp == NULL)
87 	ret = errno;
88       /* We need the limit of the stack in any case.  */
89       else
90 	{
91 	  if (__getrlimit (RLIMIT_STACK, &rl) != 0)
92 	    ret = errno;
93 	  else
94 	    {
95 	      /* We consider the main process stack to have ended with
96 	         the page containing __libc_stack_end.  There is stuff below
97 		 it in the stack too, like the program arguments, environment
98 		 variables and auxv info, but we ignore those pages when
99 		 returning size so that the output is consistent when the
100 		 stack is marked executable due to a loaded DSO requiring
101 		 it.  */
102 	      void *stack_end = (void *) ((uintptr_t) __libc_stack_end
103 					  & -(uintptr_t) GLRO(dl_pagesize));
104 #if _STACK_GROWS_DOWN
105 	      stack_end += GLRO(dl_pagesize);
106 #endif
107 	      /* We need no locking.  */
108 	      __fsetlocking (fp, FSETLOCKING_BYCALLER);
109 
110 	      /* Until we found an entry (which should always be the case)
111 		 mark the result as a failure.  */
112 	      ret = ENOENT;
113 
114 	      char *line = NULL;
115 	      size_t linelen = 0;
116 #if _STACK_GROWS_DOWN
117 	      uintptr_t last_to = 0;
118 #endif
119 
120 	      while (! feof_unlocked (fp))
121 		{
122 		  if (__getline (&line, &linelen, fp) <= 0)
123 		    break;
124 
125 		  uintptr_t from;
126 		  uintptr_t to;
127 		  if (sscanf (line, "%" SCNxPTR "-%" SCNxPTR, &from, &to) != 2)
128 		    continue;
129 		  if (from <= (uintptr_t) __libc_stack_end
130 		      && (uintptr_t) __libc_stack_end < to)
131 		    {
132 		      /* Found the entry.  Now we have the info we need.  */
133 		      iattr->stackaddr = stack_end;
134 		      iattr->stacksize =
135 		        rl.rlim_cur - (size_t) (to - (uintptr_t) stack_end);
136 
137 		      /* Cut it down to align it to page size since otherwise we
138 		         risk going beyond rlimit when the kernel rounds up the
139 		         stack extension request.  */
140 		      iattr->stacksize = (iattr->stacksize
141 					  & -(intptr_t) GLRO(dl_pagesize));
142 #if _STACK_GROWS_DOWN
143 		      /* The limit might be too high.  */
144 		      if ((size_t) iattr->stacksize
145 			  > (size_t) iattr->stackaddr - last_to)
146 			iattr->stacksize = (size_t) iattr->stackaddr - last_to;
147 #else
148 		      /* The limit might be too high.  */
149 		      if ((size_t) iattr->stacksize
150 			  > to - (size_t) iattr->stackaddr)
151 			iattr->stacksize = to - (size_t) iattr->stackaddr;
152 #endif
153 		      /* We succeed and no need to look further.  */
154 		      ret = 0;
155 		      break;
156 		    }
157 #if _STACK_GROWS_DOWN
158 		  last_to = to;
159 #endif
160 		}
161 
162 	      free (line);
163 	    }
164 
165 	  fclose (fp);
166 	}
167     }
168 
169   iattr->flags |= ATTR_FLAG_STACKADDR;
170 
171   if (ret == 0)
172     {
173       size_t size = 16;
174       cpu_set_t *cpuset = NULL;
175 
176       do
177 	{
178 	  size <<= 1;
179 
180 	  void *newp = realloc (cpuset, size);
181 	  if (newp == NULL)
182 	    {
183 	      ret = ENOMEM;
184 	      break;
185 	    }
186 	  cpuset = (cpu_set_t *) newp;
187 
188 	  ret = __pthread_getaffinity_np (thread_id, size, cpuset);
189 	}
190       /* Pick some ridiculous upper limit.  Is 8 million CPUs enough?  */
191       while (ret == EINVAL && size < 1024 * 1024);
192 
193       if (ret == 0)
194 	ret = __pthread_attr_setaffinity_np (attr, size, cpuset);
195       else if (ret == ENOSYS)
196 	/* There is no such functionality.  */
197 	ret = 0;
198       free (cpuset);
199     }
200 
201   lll_unlock (thread->lock, LLL_PRIVATE);
202 
203   if (ret != 0)
204     __pthread_attr_destroy (attr);
205 
206   return ret;
207 }
208 versioned_symbol (libc, __pthread_getattr_np, pthread_getattr_np, GLIBC_2_32);
209 
210 #if SHLIB_COMPAT (libc, GLIBC_2_2_3, GLIBC_2_32)
211 strong_alias (__pthread_getattr_np, __pthread_getattr_np_alias)
212 compat_symbol (libc, __pthread_getattr_np_alias,
213 	       pthread_getattr_np, GLIBC_2_2_3);
214 #endif
215