libc: Fix longjmp/_longjmp(buf, 0) for AArch64 and RISC-V

These architectures fail to handle this special case, and will cause the
corresponding setjmp/_setjmp to return 0 rather than 1. Fix this and add
regression tests (also committed upstream).

PR:		268684
Reviewed by:	arichardson, jhb
MFC after:	1 week
Differential Revision:	https://reviews.freebsd.org/D29363
This commit is contained in:
Jessica Clarke 2023-01-09 18:34:43 +00:00
parent 23ed2a38c2
commit 9fb118bebc
5 changed files with 55 additions and 7 deletions

View File

@ -70,6 +70,7 @@ __RCSID("$NetBSD: t_setjmp.c,v 1.2 2017/01/14 21:08:17 christos Exp $");
#include <errno.h>
#include <setjmp.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -83,6 +84,8 @@ __RCSID("$NetBSD: t_setjmp.c,v 1.2 2017/01/14 21:08:17 christos Exp $");
#define TEST_U_SETJMP 1
#define TEST_SIGSETJMP_SAVE 2
#define TEST_SIGSETJMP_NOSAVE 3
#define TEST_LONGJMP_ZERO 4
#define TEST_U_LONGJMP_ZERO 5
static int expectsignal;
@ -101,12 +104,16 @@ h_check(int test)
sigjmp_buf sjb;
sigset_t ss;
int i, x;
volatile bool did_longjmp;
i = getpid();
did_longjmp = false;
if (test == TEST_SETJMP || test == TEST_SIGSETJMP_SAVE)
if (test == TEST_SETJMP || test == TEST_SIGSETJMP_SAVE ||
test == TEST_LONGJMP_ZERO)
expectsignal = 0;
else if (test == TEST_U_SETJMP || test == TEST_SIGSETJMP_NOSAVE)
else if (test == TEST_U_SETJMP || test == TEST_SIGSETJMP_NOSAVE ||
test == TEST_U_LONGJMP_ZERO)
expectsignal = 1;
else
atf_tc_fail("unknown test");
@ -119,26 +126,37 @@ h_check(int test)
REQUIRE_ERRNO(sigaddset(&ss, SIGABRT) != -1);
REQUIRE_ERRNO(sigprocmask(SIG_BLOCK, &ss, NULL) != -1);
if (test == TEST_SETJMP)
if (test == TEST_SETJMP || test == TEST_LONGJMP_ZERO)
x = setjmp(jb);
else if (test == TEST_U_SETJMP)
else if (test == TEST_U_SETJMP || test == TEST_U_LONGJMP_ZERO)
x = _setjmp(jb);
else
x = sigsetjmp(sjb, !expectsignal);
if (x != 0) {
ATF_REQUIRE_MSG(x == i, "setjmp returned wrong value");
if (test == TEST_LONGJMP_ZERO || test == TEST_U_LONGJMP_ZERO)
ATF_REQUIRE_MSG(x == 1, "setjmp returned wrong value");
else
ATF_REQUIRE_MSG(x == i, "setjmp returned wrong value");
kill(i, SIGABRT);
ATF_REQUIRE_MSG(!expectsignal, "kill(SIGABRT) failed");
atf_tc_pass();
} else if (did_longjmp) {
atf_tc_fail("setjmp returned zero after longjmp");
}
REQUIRE_ERRNO(sigprocmask(SIG_UNBLOCK, &ss, NULL) != -1);
did_longjmp = true;
if (test == TEST_SETJMP)
longjmp(jb, i);
else if (test == TEST_LONGJMP_ZERO)
longjmp(jb, 0);
else if (test == TEST_U_SETJMP)
_longjmp(jb, i);
else if (test == TEST_U_LONGJMP_ZERO)
_longjmp(jb, 0);
else
siglongjmp(sjb, i);
@ -185,12 +203,34 @@ ATF_TC_BODY(sigsetjmp_nosave, tc)
h_check(TEST_SIGSETJMP_NOSAVE);
}
ATF_TC(longjmp_zero);
ATF_TC_HEAD(longjmp_zero, tc)
{
atf_tc_set_md_var(tc, "descr", "Checks longjmp(3) with a zero value");
}
ATF_TC_BODY(longjmp_zero, tc)
{
h_check(TEST_LONGJMP_ZERO);
}
ATF_TC(_longjmp_zero);
ATF_TC_HEAD(_longjmp_zero, tc)
{
atf_tc_set_md_var(tc, "descr", "Checks _longjmp(3) with a zero value");
}
ATF_TC_BODY(_longjmp_zero, tc)
{
h_check(TEST_U_LONGJMP_ZERO);
}
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, setjmp);
ATF_TP_ADD_TC(tp, _setjmp);
ATF_TP_ADD_TC(tp, sigsetjmp_save);
ATF_TP_ADD_TC(tp, sigsetjmp_nosave);
ATF_TP_ADD_TC(tp, longjmp_zero);
ATF_TP_ADD_TC(tp, _longjmp_zero);
return atf_no_error();
}

View File

@ -91,7 +91,8 @@ ENTRY(_longjmp)
#endif
/* Load the return value */
mov x0, x1
cmp x1, #0
csinc x0, x1, xzr, ne
ret
botch:

View File

@ -113,7 +113,8 @@ ENTRY(longjmp)
ldp d14, d15, [x0]
/* Load the return value */
mov x0, x1
cmp x1, #0
csinc x0, x1, xzr, ne
ret
botch:

View File

@ -131,6 +131,9 @@ ENTRY(_longjmp)
/* Load the return value */
mv a0, a1
bnez a1, 1f
li a0, 1
1:
ret
botch:

View File

@ -161,6 +161,9 @@ ENTRY(longjmp)
/* Load the return value */
mv a0, a1
bnez a1, 1f
li a0, 1
1:
ret
botch: