[LTP] [PATCH v3] openposix: timer_*/speculative: fork() to isolate the SIGSEGV on old glibc
Avinesh Kumar
avinesh.kumar@suse.com
Wed Sep 30 10:46:00 CEST 2026
From: Avinesh Kumar <avinesh.kumar@suse.com>
Commit d377d1cff1ec ("openposix: timer_*/speculative: Don't pass a
stack pointer as timerid") fixed a SIGSEGV on i586, where current
glibc's sign-bit check (timer_is_sigev_thread()) can misread a stack
address as a tagged pointer. A small constant avoids that.
But older glibc (<= 2.31, before this check existed) unconditionally
dereferences timerid as a pointer, so the same small constant now
faults instead:
timer_delete_sp[13131]: segfault at 186a3 ip 00007f15973aed4e sp 00007ffefb52c5a0 error 4 in librt-2.31.so[7f15973aa000+8000]
fork() a child to make the call, then inspect the child's exit status.
POSIX defines no required behavior for an invalid timer ID (EINVAL is
only a recommendation), so the child being killed by SIGSEGV or SIGBUS
is treated as success.
Fixes: d377d1cff1ec ("openposix: timer_*/speculative: Don't pass a stack pointer as timerid")
Suggested-by: Martin Doucha <mdoucha@suse.cz>
Signed-off-by: Avinesh Kumar <avinesh.kumar@suse.com>
---
.../interfaces/timer_delete/speculative/5-1.c | 56 +++++++++++-----
.../timer_getoverrun/speculative/6-1.c | 55 +++++++++++-----
.../timer_gettime/speculative/6-1.c | 58 +++++++++++-----
.../timer_settime/speculative/12-1.c | 66 +++++++++++++------
4 files changed, 169 insertions(+), 66 deletions(-)
diff --git a/testcases/open_posix_testsuite/conformance/interfaces/timer_delete/speculative/5-1.c b/testcases/open_posix_testsuite/conformance/interfaces/timer_delete/speculative/5-1.c
index dce9277b5d6a..8e7d1b2e54dc 100644
--- a/testcases/open_posix_testsuite/conformance/interfaces/timer_delete/speculative/5-1.c
+++ b/testcases/open_posix_testsuite/conformance/interfaces/timer_delete/speculative/5-1.c
@@ -5,34 +5,58 @@
* of this license, see the COPYING file at the top level of this
* source tree.
- * Test to see if timer_delete() returns -1 and sets errno==EINVAL if
- * timerid is not a valid timer ID or not.
- * Since this is a "may" requirement, either option is a PASS.
+ * Test to see if timer_delete() sets errno==EINVAL for an invalid
+ * timerid. Recent POSIX versions leave this behavior undefined, so
+ * returning -1 with EINVAL, succeeding, or crashing (older glibc
+ * dereferences timerid as a pointer) are all accepted outcomes.
*/
#include <time.h>
#include <stdio.h>
#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/wait.h>
#include "posixtest.h"
#define BOGUSTIMERID 99999
int test_main(int argc PTS_ATTRIBUTE_UNUSED, char **argv PTS_ATTRIBUTE_UNUSED)
{
- timer_t tid = (timer_t)BOGUSTIMERID;
-
- if (timer_delete(tid) == -1) {
- if (errno == EINVAL) {
- printf
- ("timer_delete() returned -1 and set errno=EINVAL\n");
- return PTS_PASS;
- } else {
- printf
- ("timer_delete() returned -1, but didn't set errno!=EINVAL\n");
- return PTS_FAIL;
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ if (pid == -1) {
+ perror("fork");
+ return PTS_UNRESOLVED;
+ }
+
+ if (pid == 0) {
+ timer_t tid = (timer_t) BOGUSTIMERID;
+
+ if (timer_delete(tid) == -1) {
+ if (errno == EINVAL)
+ _exit(PTS_PASS);
+ printf("timer_delete() returned -1, but errno=%d (%s), not EINVAL\n", errno, strerror(errno));
+ fflush(stdout);
+ _exit(PTS_FAIL);
}
+ _exit(PTS_PASS);
+ }
+
+ if (waitpid(pid, &status, 0) == -1) {
+ perror("waitpid");
+ return PTS_UNRESOLVED;
+ }
+
+ if ((WIFSIGNALED(status) && (WTERMSIG(status) == SIGSEGV || WTERMSIG(status) == SIGBUS)) ||
+ (WIFEXITED(status) && WEXITSTATUS(status) == PTS_PASS)) {
+ printf("Test PASSED\n");
+ return PTS_PASS;
}
- printf("timer_delete() did not return -1\n");
- return PTS_PASS;
+ printf("Test FAILED\n");
+ return PTS_FAIL;
}
diff --git a/testcases/open_posix_testsuite/conformance/interfaces/timer_getoverrun/speculative/6-1.c b/testcases/open_posix_testsuite/conformance/interfaces/timer_getoverrun/speculative/6-1.c
index 3a8f1448d4d4..fbc0edc38f94 100644
--- a/testcases/open_posix_testsuite/conformance/interfaces/timer_getoverrun/speculative/6-1.c
+++ b/testcases/open_posix_testsuite/conformance/interfaces/timer_getoverrun/speculative/6-1.c
@@ -5,33 +5,58 @@
* of this license, see the COPYING file at the top level of this
* source tree.
*
- * Test to see if timer_getoverrun() sets errno=EINVAL if no timers have been
- * created yet or not. Since this is a "may" assertion, either way is
- * a pass.
+ * Test to see if timer_getoverrun() sets errno==EINVAL for an invalid
+ * timerid. Recent POSIX versions leave this behavior undefined, so
+ * returning -1 with EINVAL, succeeding, or crashing (older glibc
+ * dereferences timerid as a pointer) are all accepted outcomes.
*/
#include <time.h>
#include <stdio.h>
#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/wait.h>
#include "posixtest.h"
#define BOGUSTID 9999
int test_main(int argc PTS_ATTRIBUTE_UNUSED, char **argv PTS_ATTRIBUTE_UNUSED)
{
- timer_t tid = (timer_t)BOGUSTID;
-
- if (timer_getoverrun(tid) == -1) {
- if (EINVAL == errno) {
- printf("fcn returned -1 and errno=EINVAL\n");
- return PTS_PASS;
- } else {
- printf("fcn returned -1, but errno!=EINVAL\n");
- printf("Test FAILED\n");
- return PTS_FAIL;
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ if (pid == -1) {
+ perror("fork");
+ return PTS_UNRESOLVED;
+ }
+
+ if (pid == 0) {
+ timer_t tid = (timer_t) BOGUSTID;
+
+ if (timer_getoverrun(tid) == -1) {
+ if (errno == EINVAL)
+ _exit(PTS_PASS);
+ printf("timer_getoverrun() returned -1, but errno=%d (%s), not EINVAL\n", errno, strerror(errno));
+ fflush(stdout);
+ _exit(PTS_FAIL);
}
+ _exit(PTS_PASS);
+ }
+
+ if (waitpid(pid, &status, 0) == -1) {
+ perror("waitpid");
+ return PTS_UNRESOLVED;
+ }
+
+ if ((WIFSIGNALED(status) && (WTERMSIG(status) == SIGSEGV || WTERMSIG(status) == SIGBUS)) ||
+ (WIFEXITED(status) && WEXITSTATUS(status) == PTS_PASS)) {
+ printf("Test PASSED\n");
+ return PTS_PASS;
}
- printf("fcn did not return -1\n");
- return PTS_PASS;
+ printf("Test FAILED\n");
+ return PTS_FAIL;
}
diff --git a/testcases/open_posix_testsuite/conformance/interfaces/timer_gettime/speculative/6-1.c b/testcases/open_posix_testsuite/conformance/interfaces/timer_gettime/speculative/6-1.c
index 586b0ed3a2b1..4bc1e7b43843 100644
--- a/testcases/open_posix_testsuite/conformance/interfaces/timer_gettime/speculative/6-1.c
+++ b/testcases/open_posix_testsuite/conformance/interfaces/timer_gettime/speculative/6-1.c
@@ -5,33 +5,59 @@
* of this license, see the COPYING file at the top level of this
* source tree.
*
- * Test to see if timer_gettime() sets errno = EINVAL if no timers have been
- * created yet and it is called with a bogus timer ID. Since this is a "may"
- * assertion, either way is pass.
+ * Test to see if timer_gettime() sets errno==EINVAL for an invalid
+ * timerid. Recent POSIX versions leave this behavior undefined, so
+ * returning -1 with EINVAL, succeeding, or crashing (older glibc
+ * dereferences timerid as a pointer) are all accepted outcomes.
*/
#include <time.h>
#include <stdio.h>
#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/wait.h>
#include "posixtest.h"
#define BOGUSTID 9999
int test_main(int argc PTS_ATTRIBUTE_UNUSED, char **argv PTS_ATTRIBUTE_UNUSED)
{
- struct itimerspec its;
- timer_t tid = (timer_t)BOGUSTID;
-
- if (timer_gettime(tid, &its) == -1) {
- if (EINVAL == errno) {
- printf("fcn returned -1 and errno==EINVAL\n");
- return PTS_PASS;
- } else {
- printf("fcn returned -1 but errno!=EINVAL\n");
- printf("Test FAILED\n");
- return PTS_FAIL;
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ if (pid == -1) {
+ perror("fork");
+ return PTS_UNRESOLVED;
+ }
+
+ if (pid == 0) {
+ struct itimerspec its;
+ timer_t tid = (timer_t) BOGUSTID;
+
+ if (timer_gettime(tid, &its) == -1) {
+ if (errno == EINVAL)
+ _exit(PTS_PASS);
+ printf("timer_gettime() returned -1, but errno=%d (%s), not EINVAL\n", errno, strerror(errno));
+ fflush(stdout);
+ _exit(PTS_FAIL);
}
+ _exit(PTS_PASS);
}
- printf("fcn did not return -1\n");
- return PTS_PASS;
+
+ if (waitpid(pid, &status, 0) == -1) {
+ perror("waitpid");
+ return PTS_UNRESOLVED;
+ }
+
+ if ((WIFSIGNALED(status) && (WTERMSIG(status) == SIGSEGV || WTERMSIG(status) == SIGBUS)) ||
+ (WIFEXITED(status) && WEXITSTATUS(status) == PTS_PASS)) {
+ printf("Test PASSED\n");
+ return PTS_PASS;
+ }
+
+ printf("Test FAILED\n");
+ return PTS_FAIL;
}
diff --git a/testcases/open_posix_testsuite/conformance/interfaces/timer_settime/speculative/12-1.c b/testcases/open_posix_testsuite/conformance/interfaces/timer_settime/speculative/12-1.c
index ac7f7bf24c39..0ae7ee28201c 100644
--- a/testcases/open_posix_testsuite/conformance/interfaces/timer_settime/speculative/12-1.c
+++ b/testcases/open_posix_testsuite/conformance/interfaces/timer_settime/speculative/12-1.c
@@ -5,36 +5,64 @@
* of this license, see the COPYING file at the top level of this
* source tree.
*
- * Test to see if timer_settime() sets errno = EINVAL if no timers have been
- * created yet. Since this is a "may" assertion, either way is a pass.
+ * Test to see if timer_settime() sets errno==EINVAL for an invalid
+ * timerid. Recent POSIX versions leave this behavior undefined, so
+ * returning -1 with EINVAL, succeeding, or crashing (older glibc
+ * dereferences timerid as a pointer) are all accepted outcomes.
*/
#include <time.h>
#include <stdio.h>
#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/wait.h>
#include "posixtest.h"
#define BOGUSTID 9999
int test_main(int argc PTS_ATTRIBUTE_UNUSED, char **argv PTS_ATTRIBUTE_UNUSED)
{
- struct itimerspec its;
- timer_t tid = (timer_t)BOGUSTID;
- its.it_interval.tv_sec = 0;
- its.it_interval.tv_nsec = 0;
- its.it_value.tv_sec = 0;
- its.it_value.tv_nsec = 0;
-
- if (timer_settime(tid, 0, &its, NULL) == -1) {
- if (EINVAL == errno) {
- printf("fcn returned -1 and errno==EINVAL\n");
- return PTS_PASS;
- } else {
- printf("fcn returned -1, but errno!=EINVAL\n");
- printf("Test FAILED\n");
- return PTS_FAIL;
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ if (pid == -1) {
+ perror("fork");
+ return PTS_UNRESOLVED;
+ }
+
+ if (pid == 0) {
+ struct itimerspec its;
+ timer_t tid = (timer_t) BOGUSTID;
+
+ its.it_interval.tv_sec = 0;
+ its.it_interval.tv_nsec = 0;
+ its.it_value.tv_sec = 0;
+ its.it_value.tv_nsec = 0;
+
+ if (timer_settime(tid, 0, &its, NULL) == -1) {
+ if (errno == EINVAL)
+ _exit(PTS_PASS);
+ printf("timer_settime() returned -1, but errno=%d (%s), not EINVAL\n", errno, strerror(errno));
+ fflush(stdout);
+ _exit(PTS_FAIL);
}
+ _exit(PTS_PASS);
}
- printf("fcn did not return -1\n");
- return PTS_PASS;
+
+ if (waitpid(pid, &status, 0) == -1) {
+ perror("waitpid");
+ return PTS_UNRESOLVED;
+ }
+
+ if ((WIFSIGNALED(status) && (WTERMSIG(status) == SIGSEGV || WTERMSIG(status) == SIGBUS)) ||
+ (WIFEXITED(status) && WEXITSTATUS(status) == PTS_PASS)) {
+ printf("Test PASSED\n");
+ return PTS_PASS;
+ }
+
+ printf("Test FAILED\n");
+ return PTS_FAIL;
}
--
2.55.0
More information about the ltp
mailing list