1 /* futex helper functions for glibc-internal use.
2    Copyright (C) 2020-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 #include <errno.h>
20 #include <sysdep.h>
21 #include <time.h>
22 #include <futex-internal.h>
23 #include <kernel-features.h>
24 
25 #ifndef __ASSUME_TIME64_SYSCALLS
26 static int
__futex_abstimed_wait_common32(unsigned int * futex_word,unsigned int expected,int op,const struct __timespec64 * abstime,int private,bool cancel)27 __futex_abstimed_wait_common32 (unsigned int* futex_word,
28                                 unsigned int expected, int op,
29                                 const struct __timespec64* abstime,
30                                 int private, bool cancel)
31 {
32   struct timespec ts32, *pts32 = NULL;
33   if (abstime != NULL)
34     {
35       ts32 = valid_timespec64_to_timespec (*abstime);
36       pts32 = &ts32;
37     }
38 
39   if (cancel)
40     return INTERNAL_SYSCALL_CANCEL (futex, futex_word, op, expected,
41                                     pts32, NULL /* Unused.  */,
42                                     FUTEX_BITSET_MATCH_ANY);
43   else
44     return INTERNAL_SYSCALL_CALL (futex, futex_word, op, expected,
45                                   pts32, NULL /* Unused.  */,
46                                   FUTEX_BITSET_MATCH_ANY);
47 }
48 #endif /* ! __ASSUME_TIME64_SYSCALLS */
49 
50 static int
__futex_abstimed_wait_common64(unsigned int * futex_word,unsigned int expected,int op,const struct __timespec64 * abstime,int private,bool cancel)51 __futex_abstimed_wait_common64 (unsigned int* futex_word,
52                                 unsigned int expected, int op,
53                                 const struct __timespec64* abstime,
54                                 int private, bool cancel)
55 {
56   if (cancel)
57     return INTERNAL_SYSCALL_CANCEL (futex_time64, futex_word, op, expected,
58 				    abstime, NULL /* Unused.  */,
59 				    FUTEX_BITSET_MATCH_ANY);
60   else
61     return INTERNAL_SYSCALL_CALL (futex_time64, futex_word, op, expected,
62 				  abstime, NULL /* Ununsed.  */,
63 				  FUTEX_BITSET_MATCH_ANY);
64 }
65 
66 static int
__futex_abstimed_wait_common(unsigned int * futex_word,unsigned int expected,clockid_t clockid,const struct __timespec64 * abstime,int private,bool cancel)67 __futex_abstimed_wait_common (unsigned int* futex_word,
68                               unsigned int expected, clockid_t clockid,
69                               const struct __timespec64* abstime,
70                               int private, bool cancel)
71 {
72   int err;
73   unsigned int clockbit;
74 
75   /* Work around the fact that the kernel rejects negative timeout values
76      despite them being valid.  */
77   if (__glibc_unlikely ((abstime != NULL) && (abstime->tv_sec < 0)))
78     return ETIMEDOUT;
79 
80   if (! lll_futex_supported_clockid (clockid))
81     return EINVAL;
82 
83   clockbit = (clockid == CLOCK_REALTIME) ? FUTEX_CLOCK_REALTIME : 0;
84   int op = __lll_private_flag (FUTEX_WAIT_BITSET | clockbit, private);
85 
86 #ifdef __ASSUME_TIME64_SYSCALLS
87   err = __futex_abstimed_wait_common64 (futex_word, expected, op, abstime,
88 					private, cancel);
89 #else
90   bool need_time64 = abstime != NULL && !in_time_t_range (abstime->tv_sec);
91   if (need_time64)
92     {
93       err = __futex_abstimed_wait_common64 (futex_word, expected, op, abstime,
94 					    private, cancel);
95       if (err == -ENOSYS)
96 	err = -EOVERFLOW;
97     }
98   else
99     err = __futex_abstimed_wait_common32 (futex_word, expected, op, abstime,
100                                           private, cancel);
101 #endif
102 
103   switch (err)
104     {
105     case 0:
106     case -EAGAIN:
107     case -EINTR:
108     case -ETIMEDOUT:
109     case -EINVAL:
110     case -EOVERFLOW:  /* Passed absolute timeout uses 64 bit time_t type, but
111                          underlying kernel does not support 64 bit time_t futex
112                          syscalls.  */
113       return -err;
114 
115     case -EFAULT: /* Must have been caused by a glibc or application bug.  */
116     case -ENOSYS: /* Must have been caused by a glibc bug.  */
117     /* No other errors are documented at this time.  */
118     default:
119       futex_fatal_error ();
120     }
121 }
122 
123 int
__futex_abstimed_wait64(unsigned int * futex_word,unsigned int expected,clockid_t clockid,const struct __timespec64 * abstime,int private)124 __futex_abstimed_wait64 (unsigned int* futex_word, unsigned int expected,
125                          clockid_t clockid,
126                          const struct __timespec64* abstime, int private)
127 {
128   return __futex_abstimed_wait_common (futex_word, expected, clockid,
129                                        abstime, private, false);
130 }
libc_hidden_def(__futex_abstimed_wait64)131 libc_hidden_def (__futex_abstimed_wait64)
132 
133 int
134 __futex_abstimed_wait_cancelable64 (unsigned int* futex_word,
135                                     unsigned int expected, clockid_t clockid,
136                                     const struct __timespec64* abstime,
137                                     int private)
138 {
139   return __futex_abstimed_wait_common (futex_word, expected, clockid,
140                                        abstime, private, true);
141 }
libc_hidden_def(__futex_abstimed_wait_cancelable64)142 libc_hidden_def (__futex_abstimed_wait_cancelable64)
143 
144 int
145 __futex_lock_pi64 (int *futex_word, clockid_t clockid,
146 		   const struct __timespec64 *abstime, int private)
147 {
148   int err;
149 
150   unsigned int clockbit = clockid == CLOCK_REALTIME
151 			  ? FUTEX_CLOCK_REALTIME : 0;
152   int op_pi2 = __lll_private_flag (FUTEX_LOCK_PI2 | clockbit, private);
153 #if __ASSUME_FUTEX_LOCK_PI2
154   /* Assume __ASSUME_TIME64_SYSCALLS since FUTEX_LOCK_PI2 was added later.  */
155   err = INTERNAL_SYSCALL_CALL (futex_time64, futex_word, op_pi2, 0, abstime);
156 #else
157   /* FUTEX_LOCK_PI does not support clock selection, so for CLOCK_MONOTONIC
158      the only option is to use FUTEX_LOCK_PI2.  */
159   int op_pi1 = __lll_private_flag (FUTEX_LOCK_PI, private);
160   int op_pi = abstime != NULL && clockid != CLOCK_REALTIME ? op_pi2 : op_pi1;
161 
162 # ifdef __ASSUME_TIME64_SYSCALLS
163   err = INTERNAL_SYSCALL_CALL (futex_time64, futex_word, op_pi, 0, abstime);
164 # else
165   bool need_time64 = abstime != NULL && !in_time_t_range (abstime->tv_sec);
166   if (need_time64)
167     err = INTERNAL_SYSCALL_CALL (futex_time64, futex_word, op_pi, 0, abstime);
168   else
169     {
170       struct timespec ts32, *pts32 = NULL;
171       if (abstime != NULL)
172 	{
173 	  ts32 = valid_timespec64_to_timespec (*abstime);
174 	  pts32 = &ts32;
175 	}
176       err = INTERNAL_SYSCALL_CALL (futex, futex_word, op_pi, 0, pts32);
177     }
178 # endif	 /* __ASSUME_TIME64_SYSCALLS */
179    /* FUTEX_LOCK_PI2 is not available on this kernel.  */
180    if (err == -ENOSYS)
181      err = -EINVAL;
182 #endif /* __ASSUME_FUTEX_LOCK_PI2  */
183 
184   switch (err)
185     {
186     case 0:
187     case -EAGAIN:
188     case -EINTR:
189     case -ETIMEDOUT:
190     case -ESRCH:
191     case -EDEADLK:
192     case -EINVAL: /* This indicates either state corruption or that the kernel
193                      found a waiter on futex address which is waiting via
194                      FUTEX_WAIT or FUTEX_WAIT_BITSET.  This is reported on
195                      some futex_lock_pi usage (pthread_mutex_timedlock for
196                      instance).  */
197       return -err;
198 
199     case -EFAULT: /* Must have been caused by a glibc or application bug.  */
200     case -ENOSYS: /* Must have been caused by a glibc bug.  */
201     /* No other errors are documented at this time.  */
202     default:
203       futex_fatal_error ();
204     }
205 }
206