rseq.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. // SPDX-License-Identifier: LGPL-2.1
  2. /*
  3. * rseq.c
  4. *
  5. * Copyright (C) 2016 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; only
  10. * version 2.1 of the License.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. */
  17. #define _GNU_SOURCE
  18. #include <errno.h>
  19. #include <sched.h>
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <string.h>
  23. #include <unistd.h>
  24. #include <syscall.h>
  25. #include <assert.h>
  26. #include <signal.h>
  27. #include <limits.h>
  28. #include <dlfcn.h>
  29. #include <stddef.h>
  30. #include <sys/auxv.h>
  31. #include <linux/auxvec.h>
  32. #include <linux/compiler.h>
  33. #include "../kselftest.h"
  34. #include "rseq.h"
  35. /*
  36. * Define weak versions to play nice with binaries that are statically linked
  37. * against a libc that doesn't support registering its own rseq.
  38. */
  39. extern __weak ptrdiff_t __rseq_offset;
  40. extern __weak unsigned int __rseq_size;
  41. extern __weak unsigned int __rseq_flags;
  42. static const ptrdiff_t *libc_rseq_offset_p = &__rseq_offset;
  43. static const unsigned int *libc_rseq_size_p = &__rseq_size;
  44. static const unsigned int *libc_rseq_flags_p = &__rseq_flags;
  45. /* Offset from the thread pointer to the rseq area. */
  46. ptrdiff_t rseq_offset;
  47. /*
  48. * Size of the registered rseq area. 0 if the registration was
  49. * unsuccessful.
  50. */
  51. unsigned int rseq_size = -1U;
  52. /* Flags used during rseq registration. */
  53. unsigned int rseq_flags;
  54. static int rseq_ownership;
  55. /* Allocate a large area for the TLS. */
  56. #define RSEQ_THREAD_AREA_ALLOC_SIZE 1024
  57. /* Original struct rseq feature size is 20 bytes. */
  58. #define ORIG_RSEQ_FEATURE_SIZE 20
  59. /* Original struct rseq allocation size is 32 bytes. */
  60. #define ORIG_RSEQ_ALLOC_SIZE 32
  61. static
  62. __thread struct rseq_abi __rseq_abi __attribute__((tls_model("initial-exec"), aligned(RSEQ_THREAD_AREA_ALLOC_SIZE))) = {
  63. .cpu_id = RSEQ_ABI_CPU_ID_UNINITIALIZED,
  64. };
  65. static int sys_rseq(struct rseq_abi *rseq_abi, uint32_t rseq_len,
  66. int flags, uint32_t sig)
  67. {
  68. return syscall(__NR_rseq, rseq_abi, rseq_len, flags, sig);
  69. }
  70. static int sys_getcpu(unsigned *cpu, unsigned *node)
  71. {
  72. return syscall(__NR_getcpu, cpu, node, NULL);
  73. }
  74. int rseq_available(void)
  75. {
  76. int rc;
  77. rc = sys_rseq(NULL, 0, 0, 0);
  78. if (rc != -1)
  79. abort();
  80. switch (errno) {
  81. case ENOSYS:
  82. return 0;
  83. case EINVAL:
  84. return 1;
  85. default:
  86. abort();
  87. }
  88. }
  89. /* The rseq areas need to be at least 32 bytes. */
  90. static
  91. unsigned int get_rseq_min_alloc_size(void)
  92. {
  93. unsigned int alloc_size = rseq_size;
  94. if (alloc_size < ORIG_RSEQ_ALLOC_SIZE)
  95. alloc_size = ORIG_RSEQ_ALLOC_SIZE;
  96. return alloc_size;
  97. }
  98. /*
  99. * Return the feature size supported by the kernel.
  100. *
  101. * Depending on the value returned by getauxval(AT_RSEQ_FEATURE_SIZE):
  102. *
  103. * 0: Return ORIG_RSEQ_FEATURE_SIZE (20)
  104. * > 0: Return the value from getauxval(AT_RSEQ_FEATURE_SIZE).
  105. *
  106. * It should never return a value below ORIG_RSEQ_FEATURE_SIZE.
  107. */
  108. static
  109. unsigned int get_rseq_kernel_feature_size(void)
  110. {
  111. unsigned long auxv_rseq_feature_size, auxv_rseq_align;
  112. auxv_rseq_align = getauxval(AT_RSEQ_ALIGN);
  113. assert(!auxv_rseq_align || auxv_rseq_align <= RSEQ_THREAD_AREA_ALLOC_SIZE);
  114. auxv_rseq_feature_size = getauxval(AT_RSEQ_FEATURE_SIZE);
  115. assert(!auxv_rseq_feature_size || auxv_rseq_feature_size <= RSEQ_THREAD_AREA_ALLOC_SIZE);
  116. if (auxv_rseq_feature_size)
  117. return auxv_rseq_feature_size;
  118. else
  119. return ORIG_RSEQ_FEATURE_SIZE;
  120. }
  121. int rseq_register_current_thread(void)
  122. {
  123. int rc;
  124. if (!rseq_ownership) {
  125. /* Treat libc's ownership as a successful registration. */
  126. return 0;
  127. }
  128. rc = sys_rseq(&__rseq_abi, get_rseq_min_alloc_size(), 0, RSEQ_SIG);
  129. if (rc) {
  130. /*
  131. * After at least one thread has registered successfully
  132. * (rseq_size > 0), the registration of other threads should
  133. * never fail.
  134. */
  135. if (RSEQ_READ_ONCE(rseq_size) > 0) {
  136. /* Incoherent success/failure within process. */
  137. abort();
  138. }
  139. return -1;
  140. }
  141. assert(rseq_current_cpu_raw() >= 0);
  142. /*
  143. * The first thread to register sets the rseq_size to mimic the libc
  144. * behavior.
  145. */
  146. if (RSEQ_READ_ONCE(rseq_size) == 0) {
  147. RSEQ_WRITE_ONCE(rseq_size, get_rseq_kernel_feature_size());
  148. }
  149. return 0;
  150. }
  151. int rseq_unregister_current_thread(void)
  152. {
  153. int rc;
  154. if (!rseq_ownership) {
  155. /* Treat libc's ownership as a successful unregistration. */
  156. return 0;
  157. }
  158. rc = sys_rseq(&__rseq_abi, get_rseq_min_alloc_size(), RSEQ_ABI_FLAG_UNREGISTER, RSEQ_SIG);
  159. if (rc)
  160. return -1;
  161. return 0;
  162. }
  163. static __attribute__((constructor))
  164. void rseq_init(void)
  165. {
  166. /*
  167. * If the libc's registered rseq size isn't already valid, it may be
  168. * because the binary is dynamically linked and not necessarily due to
  169. * libc not having registered a restartable sequence. Try to find the
  170. * symbols if that's the case.
  171. */
  172. if (!libc_rseq_size_p || !*libc_rseq_size_p) {
  173. libc_rseq_offset_p = dlsym(RTLD_NEXT, "__rseq_offset");
  174. libc_rseq_size_p = dlsym(RTLD_NEXT, "__rseq_size");
  175. libc_rseq_flags_p = dlsym(RTLD_NEXT, "__rseq_flags");
  176. }
  177. if (libc_rseq_size_p && libc_rseq_offset_p && libc_rseq_flags_p &&
  178. *libc_rseq_size_p != 0) {
  179. unsigned int libc_rseq_size;
  180. /* rseq registration owned by glibc */
  181. rseq_offset = *libc_rseq_offset_p;
  182. libc_rseq_size = *libc_rseq_size_p;
  183. rseq_flags = *libc_rseq_flags_p;
  184. /*
  185. * Previous versions of glibc expose the value
  186. * 32 even though the kernel only supported 20
  187. * bytes initially. Therefore treat 32 as a
  188. * special-case. glibc 2.40 exposes a 20 bytes
  189. * __rseq_size without using getauxval(3) to
  190. * query the supported size, while still allocating a 32
  191. * bytes area. Also treat 20 as a special-case.
  192. *
  193. * Special-cases are handled by using the following
  194. * value as active feature set size:
  195. *
  196. * rseq_size = min(32, get_rseq_kernel_feature_size())
  197. */
  198. switch (libc_rseq_size) {
  199. case ORIG_RSEQ_FEATURE_SIZE:
  200. fallthrough;
  201. case ORIG_RSEQ_ALLOC_SIZE:
  202. {
  203. unsigned int rseq_kernel_feature_size = get_rseq_kernel_feature_size();
  204. if (rseq_kernel_feature_size < ORIG_RSEQ_ALLOC_SIZE)
  205. rseq_size = rseq_kernel_feature_size;
  206. else
  207. rseq_size = ORIG_RSEQ_ALLOC_SIZE;
  208. break;
  209. }
  210. default:
  211. /* Otherwise just use the __rseq_size from libc as rseq_size. */
  212. rseq_size = libc_rseq_size;
  213. break;
  214. }
  215. return;
  216. }
  217. rseq_ownership = 1;
  218. /* Calculate the offset of the rseq area from the thread pointer. */
  219. rseq_offset = (void *)&__rseq_abi - rseq_thread_pointer();
  220. /* rseq flags are deprecated, always set to 0. */
  221. rseq_flags = 0;
  222. /*
  223. * Set the size to 0 until at least one thread registers to mimic the
  224. * libc behavior.
  225. */
  226. rseq_size = 0;
  227. }
  228. static __attribute__((destructor))
  229. void rseq_exit(void)
  230. {
  231. if (!rseq_ownership)
  232. return;
  233. rseq_offset = 0;
  234. rseq_size = -1U;
  235. rseq_ownership = 0;
  236. }
  237. int32_t rseq_fallback_current_cpu(void)
  238. {
  239. int32_t cpu;
  240. cpu = sched_getcpu();
  241. if (cpu < 0) {
  242. perror("sched_getcpu()");
  243. abort();
  244. }
  245. return cpu;
  246. }
  247. int32_t rseq_fallback_current_node(void)
  248. {
  249. uint32_t cpu_id, node_id;
  250. int ret;
  251. ret = sys_getcpu(&cpu_id, &node_id);
  252. if (ret) {
  253. perror("sys_getcpu()");
  254. return ret;
  255. }
  256. return (int32_t) node_id;
  257. }