1From 9171c2495b1a05eabaeb0afeee629682cc6f5bac Mon Sep 17 00:00:00 2001
2From: Adhemerval Zanella <adhemerval.zanella@linaro.org>
3Date: Mon, 5 Oct 2020 17:30:05 -0300
4Subject: [PATCH 15/20] posix: Fix -Warray-bounds instances building
5 timer_create [BZ #26687]
6
7GCC 11 -Warray-bounds triggers invalid warnings when building
8Linux timer_create.c:
9
10../sysdeps/unix/sysv/linux/timer_create.c: In function '__timer_create_new':
11../sysdeps/unix/sysv/linux/timer_create.c:83:17: warning: array subscript 'struct timer[0]' is partly outside array bounds of 'unsigned char[8]' [-Warray-bounds]
12   83 |             newp->sigev_notify = (evp != NULL
13      |                 ^~
14../sysdeps/unix/sysv/linux/timer_create.c:59:47: note: referencing an object of size 8 allocated by 'malloc'
15   59 |         struct timer *newp = (struct timer *) malloc (offsetof (struct timer,
16      |                                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17   60 |                                                                 thrfunc));
18      |                                                                 ~~~~~~~~~
19
20The struct allocated for !SIGEV_THREAD timers only requires two 'int'
21fields (sigev_notify and ktimerid) and the offsetof trick tries minimize
22the memory usage by only allocation the required size.  However,
23although the resulting size is suffice for !SIGEV_THREAD time, accessing
24the partially allocated object is error-prone and UB.
25
26This patch fixes both issues by embedding the information whether
27the timer if a SIGEV_THREAD in the returned 'timer_t'.  For
28!SIGEV_THREAD, the resulting 'timer_t' is the returned kernel timer
29identifer (kernel_timer_t), while for SIGEV_THREAD it uses the fact
30malloc returns at least _Alignof (max_align_t) pointers plus that
31valid kernel_timer_t are always positive to set MSB bit of the returned
32'timer_t' to indicate the timer handles a SIGEV_THREAD.
33
34It allows to remove the memory allocation for !SIGEV_THREAD and also
35remove the 'sigev_notify' field from 'struct timer'.
36
37Checked on x86_64-linux-gnu and i686-linux-gnu.
38
39(cherry picked from commit 7a887dd537cd00fe3cdf42b788b3f0e3b430b0ed)
40Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
41---
42 sysdeps/unix/sysv/linux/kernel-posix-timers.h | 52 ++++++++++---
43 sysdeps/unix/sysv/linux/timer_create.c        | 74 ++++++-------------
44 sysdeps/unix/sysv/linux/timer_delete.c        | 15 ++--
45 sysdeps/unix/sysv/linux/timer_getoverr.c      |  8 +-
46 sysdeps/unix/sysv/linux/timer_gettime.c       |  4 +-
47 sysdeps/unix/sysv/linux/timer_settime.c       |  4 +-
48 6 files changed, 76 insertions(+), 81 deletions(-)
49
50diff --git a/sysdeps/unix/sysv/linux/kernel-posix-timers.h b/sysdeps/unix/sysv/linux/kernel-posix-timers.h
51index 31f51997..ad998235 100644
52--- a/sysdeps/unix/sysv/linux/kernel-posix-timers.h
53+++ b/sysdeps/unix/sysv/linux/kernel-posix-timers.h
54@@ -43,21 +43,11 @@ extern pthread_mutex_t __active_timer_sigev_thread_lock attribute_hidden;
55 /* Type of timers in the kernel.  */
56 typedef int kernel_timer_t;
57
58-
59-/* Internal representation of timer.  */
60+/* Internal representation of SIGEV_THREAD timer.  */
61 struct timer
62 {
63-  /* Notification mechanism.  */
64-  int sigev_notify;
65-
66-  /* Timer ID returned by the kernel.  */
67   kernel_timer_t ktimerid;
68
69-  /* All new elements must be added after ktimerid.  And if the thrfunc
70-     element is not the third element anymore the memory allocation in
71-     timer_create needs to be changed.  */
72-
73-  /* Parameters for the thread to be started for SIGEV_THREAD.  */
74   void (*thrfunc) (sigval_t);
75   sigval_t sival;
76   pthread_attr_t attr;
77@@ -65,3 +55,43 @@ struct timer
78   /* Next element in list of active SIGEV_THREAD timers.  */
79   struct timer *next;
80 };
81+
82+
83+/* For !SIGEV_THREAD, the resulting 'timer_t' is the returned kernel timer
84+   identifer (kernel_timer_t), while for SIGEV_THREAD it uses the fact malloc
85+   returns at least _Alignof (max_align_t) pointers plus that valid
86+   kernel_timer_t are always positive to set the MSB bit of the returned
87+   'timer_t' to indicate the timer handles a SIGEV_THREAD.  */
88+
89+static inline timer_t
90+kernel_timer_to_timerid (kernel_timer_t ktimerid)
91+{
92+  return (timer_t) ((intptr_t) ktimerid);
93+}
94+
95+static inline timer_t
96+timer_to_timerid (struct timer *ptr)
97+{
98+  return (timer_t) (INTPTR_MIN | (uintptr_t) ptr >> 1);
99+}
100+
101+static inline bool
102+timer_is_sigev_thread (timer_t timerid)
103+{
104+  return (intptr_t) timerid < 0;
105+}
106+
107+static inline struct timer *
108+timerid_to_timer (timer_t timerid)
109+{
110+  return (struct timer *)((uintptr_t) timerid << 1);
111+}
112+
113+static inline kernel_timer_t
114+timerid_to_kernel_timer (timer_t timerid)
115+{
116+  if (timer_is_sigev_thread (timerid))
117+    return timerid_to_timer (timerid)->ktimerid;
118+  else
119+    return (kernel_timer_t) ((uintptr_t) timerid);
120+}
121diff --git a/sysdeps/unix/sysv/linux/timer_create.c b/sysdeps/unix/sysv/linux/timer_create.c
122index a49a546e..ad4b0d46 100644
123--- a/sysdeps/unix/sysv/linux/timer_create.c
124+++ b/sysdeps/unix/sysv/linux/timer_create.c
125@@ -52,16 +52,6 @@ timer_create (clockid_t clock_id, struct sigevent *evp, timer_t *timerid)
126       {
127 	struct sigevent local_evp;
128
129-	/* We avoid allocating too much memory by basically
130-	   using struct timer as a derived class with the
131-	   first two elements being in the superclass.  We only
132-	   need these two elements here.  */
133-	struct timer *newp = (struct timer *) malloc (offsetof (struct timer,
134-								thrfunc));
135-	if (newp == NULL)
136-	  /* No more memory.  */
137-	  return -1;
138-
139 	if (evp == NULL)
140 	  {
141 	    /* The kernel has to pass up the timer ID which is a
142@@ -69,31 +59,17 @@ timer_create (clockid_t clock_id, struct sigevent *evp, timer_t *timerid)
143 	       the kernel to determine it.  */
144 	    local_evp.sigev_notify = SIGEV_SIGNAL;
145 	    local_evp.sigev_signo = SIGALRM;
146-	    local_evp.sigev_value.sival_ptr = newp;
147+	    local_evp.sigev_value.sival_ptr = NULL;
148
149 	    evp = &local_evp;
150 	  }
151
152 	kernel_timer_t ktimerid;
153-	int retval = INLINE_SYSCALL (timer_create, 3, syscall_clockid, evp,
154-				     &ktimerid);
155-
156-	if (retval != -1)
157-	  {
158-	    newp->sigev_notify = (evp != NULL
159-				  ? evp->sigev_notify : SIGEV_SIGNAL);
160-	    newp->ktimerid = ktimerid;
161-
162-	    *timerid = (timer_t) newp;
163-	  }
164-	else
165-	  {
166-	    /* Cannot allocate the timer, fail.  */
167-	    free (newp);
168-	    retval = -1;
169-	  }
170+	if (INLINE_SYSCALL_CALL (timer_create, syscall_clockid, evp,
171+				 &ktimerid) == -1)
172+	  return -1;
173
174-	return retval;
175+	*timerid = kernel_timer_to_timerid (ktimerid);
176       }
177     else
178       {
179@@ -106,20 +82,18 @@ timer_create (clockid_t clock_id, struct sigevent *evp, timer_t *timerid)
180 	    return -1;
181 	  }
182
183-	struct timer *newp;
184-	newp = (struct timer *) malloc (sizeof (struct timer));
185+	struct timer *newp = malloc (sizeof (struct timer));
186 	if (newp == NULL)
187 	  return -1;
188
189 	/* Copy the thread parameters the user provided.  */
190 	newp->sival = evp->sigev_value;
191 	newp->thrfunc = evp->sigev_notify_function;
192-	newp->sigev_notify = SIGEV_THREAD;
193
194 	/* We cannot simply copy the thread attributes since the
195 	   implementation might keep internal information for
196 	   each instance.  */
197-	(void) pthread_attr_init (&newp->attr);
198+	pthread_attr_init (&newp->attr);
199 	if (evp->sigev_notify_attributes != NULL)
200 	  {
201 	    struct pthread_attr *nattr;
202@@ -137,8 +111,7 @@ timer_create (clockid_t clock_id, struct sigevent *evp, timer_t *timerid)
203 	  }
204
205 	/* In any case set the detach flag.  */
206-	(void) pthread_attr_setdetachstate (&newp->attr,
207-					    PTHREAD_CREATE_DETACHED);
208+	pthread_attr_setdetachstate (&newp->attr, PTHREAD_CREATE_DETACHED);
209
210 	/* Create the event structure for the kernel timer.  */
211 	struct sigevent sev =
212@@ -150,27 +123,24 @@ timer_create (clockid_t clock_id, struct sigevent *evp, timer_t *timerid)
213 	/* Create the timer.  */
214 	INTERNAL_SYSCALL_DECL (err);
215 	int res;
216-	res = INTERNAL_SYSCALL (timer_create, err, 3,
217-				syscall_clockid, &sev, &newp->ktimerid);
218-	if (! INTERNAL_SYSCALL_ERROR_P (res, err))
219+	res = INTERNAL_SYSCALL_CALL (timer_create, syscall_clockid, &sev,
220+				     &newp->ktimerid);
221+	if (INTERNAL_SYSCALL_ERROR_P (res, err))
222 	  {
223-	    /* Add to the queue of active timers with thread
224-	       delivery.  */
225-	    pthread_mutex_lock (&__active_timer_sigev_thread_lock);
226-	    newp->next = __active_timer_sigev_thread;
227-	    __active_timer_sigev_thread = newp;
228-	    pthread_mutex_unlock (&__active_timer_sigev_thread_lock);
229-
230-	    *timerid = (timer_t) newp;
231-	    return 0;
232+	    free (newp);
233+	    __set_errno (INTERNAL_SYSCALL_ERRNO (res, err));
234+	    return -1;
235 	  }
236
237-	/* Free the resources.  */
238-	free (newp);
239-
240-	__set_errno (INTERNAL_SYSCALL_ERRNO (res, err));
241+	/* Add to the queue of active timers with thread delivery.  */
242+	pthread_mutex_lock (&__active_timer_sigev_thread_lock);
243+	newp->next = __active_timer_sigev_thread;
244+	__active_timer_sigev_thread = newp;
245+	pthread_mutex_unlock (&__active_timer_sigev_thread_lock);
246
247-	return -1;
248+	*timerid = timer_to_timerid (newp);
249       }
250   }
251+
252+  return 0;
253 }
254diff --git a/sysdeps/unix/sysv/linux/timer_delete.c b/sysdeps/unix/sysv/linux/timer_delete.c
255index e1eb1db5..be459dc3 100644
256--- a/sysdeps/unix/sysv/linux/timer_delete.c
257+++ b/sysdeps/unix/sysv/linux/timer_delete.c
258@@ -32,15 +32,15 @@ int
259 timer_delete (timer_t timerid)
260 {
261 #undef timer_delete
262-  struct timer *kt = (struct timer *) timerid;
263-
264-  /* Delete the kernel timer object.  */
265-  int res = INLINE_SYSCALL (timer_delete, 1, kt->ktimerid);
266+  kernel_timer_t ktimerid = timerid_to_kernel_timer (timerid);
267+  int res = INLINE_SYSCALL_CALL (timer_delete, ktimerid);
268
269   if (res == 0)
270     {
271-      if (kt->sigev_notify == SIGEV_THREAD)
272+      if (timer_is_sigev_thread (timerid))
273 	{
274+	  struct timer *kt = timerid_to_timer (timerid);
275+
276 	  /* Remove the timer from the list.  */
277 	  pthread_mutex_lock (&__active_timer_sigev_thread_lock);
278 	  if (__active_timer_sigev_thread == kt)
279@@ -58,10 +58,9 @@ timer_delete (timer_t timerid)
280 		  prevp = prevp->next;
281 	    }
282 	  pthread_mutex_unlock (&__active_timer_sigev_thread_lock);
283-	}
284
285-      /* Free the memory.  */
286-      (void) free (kt);
287+	  free (kt);
288+	}
289
290       return 0;
291     }
292diff --git a/sysdeps/unix/sysv/linux/timer_getoverr.c b/sysdeps/unix/sysv/linux/timer_getoverr.c
293index 26f23e1d..0dfcbe81 100644
294--- a/sysdeps/unix/sysv/linux/timer_getoverr.c
295+++ b/sysdeps/unix/sysv/linux/timer_getoverr.c
296@@ -31,10 +31,6 @@ int
297 timer_getoverrun (timer_t timerid)
298 {
299 #undef timer_getoverrun
300-  struct timer *kt = (struct timer *) timerid;
301-
302-  /* Get the information from the kernel.  */
303-  int res = INLINE_SYSCALL (timer_getoverrun, 1, kt->ktimerid);
304-
305-  return res;
306+  kernel_timer_t ktimerid = timerid_to_kernel_timer (timerid);
307+  return INLINE_SYSCALL_CALL (timer_getoverrun, ktimerid);
308 }
309diff --git a/sysdeps/unix/sysv/linux/timer_gettime.c b/sysdeps/unix/sysv/linux/timer_gettime.c
310index 10a19d9e..a3e1981a 100644
311--- a/sysdeps/unix/sysv/linux/timer_gettime.c
312+++ b/sysdeps/unix/sysv/linux/timer_gettime.c
313@@ -32,10 +32,10 @@ int
314 timer_gettime (timer_t timerid, struct itimerspec *value)
315 {
316 #undef timer_gettime
317-  struct timer *kt = (struct timer *) timerid;
318+  kernel_timer_t ktimerid = timerid_to_kernel_timer (timerid);
319
320   /* Delete the kernel timer object.  */
321-  int res = INLINE_SYSCALL (timer_gettime, 2, kt->ktimerid, value);
322+  int res = INLINE_SYSCALL (timer_gettime, 2, ktimerid, value);
323
324   return res;
325 }
326diff --git a/sysdeps/unix/sysv/linux/timer_settime.c b/sysdeps/unix/sysv/linux/timer_settime.c
327index 7c938bd4..7038e2a7 100644
328--- a/sysdeps/unix/sysv/linux/timer_settime.c
329+++ b/sysdeps/unix/sysv/linux/timer_settime.c
330@@ -33,10 +33,10 @@ timer_settime (timer_t timerid, int flags, const struct itimerspec *value,
331 	       struct itimerspec *ovalue)
332 {
333 #undef timer_settime
334-  struct timer *kt = (struct timer *) timerid;
335+  kernel_timer_t ktimerid = timerid_to_kernel_timer (timerid);
336
337   /* Delete the kernel timer object.  */
338-  int res = INLINE_SYSCALL (timer_settime, 4, kt->ktimerid, flags,
339+  int res = INLINE_SYSCALL (timer_settime, 4, ktimerid, flags,
340 			    value, ovalue);
341
342   return res;
343--
3442.20.1
345
346