// SPDX-License-Identifier: LGPL-2.1 /* * rseq.c * * Copyright (C) 2016 Mathieu Desnoyers <mathieu.desnoyers@efficios.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; only * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details.
*/
/* * Define weak versions to play nice with binaries that are statically linked * against a libc that doesn't support registering its own rseq.
*/ extern __weak ptrdiff_t __rseq_offset; extern __weak unsignedint __rseq_size; extern __weak unsignedint __rseq_flags;
/* Offset from the thread pointer to the rseq area. */
ptrdiff_t rseq_offset;
/* * Size of the registered rseq area. 0 if the registration was * unsuccessful.
*/ unsignedint rseq_size = -1U;
/* Flags used during rseq registration. */ unsignedint rseq_flags;
staticint rseq_ownership;
/* Allocate a large area for the TLS. */ #define RSEQ_THREAD_AREA_ALLOC_SIZE 1024
/* Original struct rseq feature size is 20 bytes. */ #define ORIG_RSEQ_FEATURE_SIZE 20
/* Original struct rseq allocation size is 32 bytes. */ #define ORIG_RSEQ_ALLOC_SIZE 32
/* * Use a union to ensure we allocate a TLS area of 1024 bytes to accomodate an * rseq registration that is larger than the current rseq ABI.
*/ union rseq_tls { struct rseq_abi abi; char dummy[RSEQ_THREAD_AREA_ALLOC_SIZE];
};
rc = sys_rseq(NULL, 0, 0, 0); if (rc != -1)
abort(); switch (errno) { case ENOSYS: returnfalse; case EINVAL: returntrue; default:
abort();
}
}
/* The rseq areas need to be at least 32 bytes. */ static unsignedint get_rseq_min_alloc_size(void)
{ unsignedint alloc_size = rseq_size;
if (alloc_size < ORIG_RSEQ_ALLOC_SIZE)
alloc_size = ORIG_RSEQ_ALLOC_SIZE; return alloc_size;
}
/* * Return the feature size supported by the kernel. * * Depending on the value returned by getauxval(AT_RSEQ_FEATURE_SIZE): * * 0: Return ORIG_RSEQ_FEATURE_SIZE (20) * > 0: Return the value from getauxval(AT_RSEQ_FEATURE_SIZE). * * It should never return a value below ORIG_RSEQ_FEATURE_SIZE.
*/ static unsignedint get_rseq_kernel_feature_size(void)
{ unsignedlong auxv_rseq_feature_size, auxv_rseq_align;
if (!rseq_ownership) { /* Treat libc's ownership as a successful registration. */ return 0;
}
rc = sys_rseq(&__rseq.abi, get_rseq_min_alloc_size(), 0, RSEQ_SIG); if (rc) { /* * After at least one thread has registered successfully * (rseq_size > 0), the registration of other threads should * never fail.
*/ if (RSEQ_READ_ONCE(rseq_size) > 0) { /* Incoherent success/failure within process. */
abort();
} return -1;
}
assert(rseq_current_cpu_raw() >= 0);
/* * The first thread to register sets the rseq_size to mimic the libc * behavior.
*/ if (RSEQ_READ_ONCE(rseq_size) == 0) {
RSEQ_WRITE_ONCE(rseq_size, get_rseq_kernel_feature_size());
}
return 0;
}
int rseq_unregister_current_thread(void)
{ int rc;
if (!rseq_ownership) { /* Treat libc's ownership as a successful unregistration. */ return 0;
}
rc = sys_rseq(&__rseq.abi, get_rseq_min_alloc_size(), RSEQ_ABI_FLAG_UNREGISTER, RSEQ_SIG); if (rc) return -1; return 0;
}
static __attribute__((constructor)) void rseq_init(void)
{ /* * If the libc's registered rseq size isn't already valid, it may be * because the binary is dynamically linked and not necessarily due to * libc not having registered a restartable sequence. Try to find the * symbols if that's the case.
*/ if (!libc_rseq_size_p || !*libc_rseq_size_p) {
libc_rseq_offset_p = dlsym(RTLD_NEXT, "__rseq_offset");
libc_rseq_size_p = dlsym(RTLD_NEXT, "__rseq_size");
libc_rseq_flags_p = dlsym(RTLD_NEXT, "__rseq_flags");
} if (libc_rseq_size_p && libc_rseq_offset_p && libc_rseq_flags_p &&
*libc_rseq_size_p != 0) { unsignedint libc_rseq_size;
/* * Previous versions of glibc expose the value * 32 even though the kernel only supported 20 * bytes initially. Therefore treat 32 as a * special-case. glibc 2.40 exposes a 20 bytes * __rseq_size without using getauxval(3) to * query the supported size, while still allocating a 32 * bytes area. Also treat 20 as a special-case. * * Special-cases are handled by using the following * value as active feature set size: * * rseq_size = min(32, get_rseq_kernel_feature_size())
*/ switch (libc_rseq_size) { case ORIG_RSEQ_FEATURE_SIZE:
fallthrough; case ORIG_RSEQ_ALLOC_SIZE:
{ unsignedint rseq_kernel_feature_size = get_rseq_kernel_feature_size();
if (rseq_kernel_feature_size < ORIG_RSEQ_ALLOC_SIZE)
rseq_size = rseq_kernel_feature_size; else
rseq_size = ORIG_RSEQ_ALLOC_SIZE; break;
} default: /* Otherwise just use the __rseq_size from libc as rseq_size. */
rseq_size = libc_rseq_size; break;
} return;
}
rseq_ownership = 1;
/* Calculate the offset of the rseq area from the thread pointer. */
rseq_offset = (void *)&__rseq.abi - rseq_thread_pointer();
/* rseq flags are deprecated, always set to 0. */
rseq_flags = 0;
/* * Set the size to 0 until at least one thread registers to mimic the * libc behavior.
*/
rseq_size = 0;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.