Skip to content

Commit 3265265

Browse files
tests/arch/x86: Add tests for shadow stack
Ensure they behave as expected. Signed-off-by: Ederson de Souza <ederson.desouza@intel.com>
1 parent 2ea1186 commit 3265265

File tree

6 files changed

+188
-26
lines changed

6 files changed

+188
-26
lines changed

tests/arch/x86/cet/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ project(cet)
66

77
enable_language(C ASM)
88

9-
target_sources(app PRIVATE src/main.c src/asm.S)
9+
target_sources(app PRIVATE src/main.c src/common.c src/asm.S)

tests/arch/x86/cet/prj.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
CONFIG_ZTEST=y
22
CONFIG_X86_CET=y
3+
CONFIG_IRQ_OFFLOAD=y

tests/arch/x86/cet/src/common.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright (c) 2025 Intel Corporation
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <zephyr/ztest.h>
8+
#include <zephyr/arch/exception.h>
9+
10+
K_SEM_DEFINE(error_handler_sem, 0, 1)
11+
12+
volatile bool expect_fault;
13+
volatile int expect_code;
14+
volatile int expect_reason;
15+
16+
void k_sys_fatal_error_handler(unsigned int reason, const struct arch_esf *pEsf)
17+
{
18+
if (expect_fault) {
19+
#ifdef CONFIG_X86_64
20+
zassert_equal(pEsf->vector, expect_reason, "unexpected exception");
21+
zassert_equal(pEsf->code, expect_code, "unexpected error code");
22+
#else
23+
zassert_equal(z_x86_exception_vector, expect_reason, "unexpected exception");
24+
zassert_equal(pEsf->errorCode, expect_code, "unexpected error code");
25+
#endif
26+
printk("fatal error expected as part of test case\n");
27+
expect_fault = false;
28+
29+
k_sem_give(&error_handler_sem);
30+
} else {
31+
printk("fatal error was unexpected, aborting\n");
32+
TC_END_REPORT(TC_FAIL);
33+
k_fatal_halt(reason);
34+
}
35+
}

tests/arch/x86/cet/src/common.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright (c) 2025 Intel Corporation
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define IV_CTRL_PROTECTION_EXCEPTION 21
8+
#define IV_IRQS 32
9+
10+
#define CTRL_PROTECTION_ERRORCODE_NEAR_RET 1
11+
#define CTRL_PROTECTION_ERRORCODE_FAR_RET_IRET 2
12+
#define CTRL_PROTECTION_ERRORCODE_ENDBRANCH 3
13+
14+
#define STACKSIZE 1024
15+
#define THREAD_PRIORITY 5
16+
17+
extern struct k_sem *error_handler_sem;
18+
19+
extern volatile bool expect_fault;
20+
extern volatile int expect_code;
21+
extern volatile int expect_reason;

tests/arch/x86/cet/src/main.c

Lines changed: 119 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,39 +5,131 @@
55
*/
66

77
#include <zephyr/ztest.h>
8-
#include <zephyr/arch/exception.h>
8+
#include <zephyr/irq_offload.h>
99

10-
#define IV_CTRL_PROTECTION_EXCEPTION 21
10+
#include <zephyr/kernel/thread_stack.h>
1111

12-
#define CTRL_PROTECTION_ERRORCODE_ENDBRANCH 3
12+
#include "common.h"
1313

14-
extern int should_work(int a);
15-
extern int should_not_work(int a);
14+
#define SHADOW_STACK_SIZE 1024
15+
16+
#ifdef CONFIG_HW_SHADOW_STACK
17+
void thread_a_entry(void *p1, void *p2, void *p3);
18+
K_SEM_DEFINE(thread_a_sem, 0, 1);
19+
K_THREAD_DEFINE(thread_a, STACKSIZE, thread_a_entry, NULL, NULL, NULL,
20+
THREAD_PRIORITY, 0, -1);
21+
22+
void thread_b_entry(void *p1, void *p2, void *p3);
23+
K_SEM_DEFINE(thread_b_sem, 0, 1);
24+
K_SEM_DEFINE(thread_b_irq_sem, 0, 1);
25+
K_THREAD_DEFINE(thread_b, STACKSIZE, thread_b_entry, NULL, NULL, NULL,
26+
THREAD_PRIORITY, 0, -1);
27+
28+
static bool is_shstk_enabled(void)
29+
{
30+
long cur;
31+
32+
cur = z_x86_msr_read(X86_S_CET_MSR);
33+
return (cur & X86_S_CET_MSR_SHSTK_EN) == X86_S_CET_MSR_SHSTK_EN;
34+
}
35+
36+
void thread_c_entry(void *p1, void *p2, void *p3)
37+
{
38+
zassert_true(is_shstk_enabled(), "shadow stack not enabled on static thread");
39+
}
40+
41+
K_THREAD_DEFINE(thread_c, STACKSIZE, thread_c_entry, NULL, NULL, NULL,
42+
THREAD_PRIORITY, 0, 0);
43+
44+
void __attribute__((optimize("O0"))) foo(void)
45+
{
46+
printk("foo called\n");
47+
}
48+
49+
void __attribute__((optimize("O0"))) fail(void)
50+
{
51+
long a[] = {0};
52+
53+
printk("should fail after this\n");
54+
55+
*(a + 2) = (long)foo;
56+
}
57+
58+
struct k_work work;
59+
60+
void work_handler(struct k_work *wrk)
61+
{
62+
printk("work handler\n");
63+
64+
zassert_true(is_shstk_enabled(), "shadow stack not enabled");
65+
}
66+
67+
ZTEST(cet, test_shstk_work_q)
68+
{
69+
k_work_init(&work, work_handler);
70+
k_work_submit(&work);
71+
}
1672

17-
static bool expect_fault;
18-
static int expect_code;
19-
20-
void k_sys_fatal_error_handler(unsigned int reason, const struct arch_esf *pEsf)
21-
{
22-
if (expect_fault) {
23-
#ifdef CONFIG_X86_64
24-
zassert_equal(pEsf->vector, IV_CTRL_PROTECTION_EXCEPTION,
25-
"unexpected exception");
26-
zassert_equal(pEsf->code, expect_code, "unexpected error code");
27-
#else
28-
zassert_equal(z_x86_exception_vector, IV_CTRL_PROTECTION_EXCEPTION,
29-
"unexpected exception");
30-
zassert_equal(pEsf->errorCode, expect_code, "unexpected error code");
31-
#endif
32-
printk("fatal error expected as part of test case\n");
33-
expect_fault = false;
73+
void intr_handler(const void *p)
74+
{
75+
printk("interrupt handler\n");
76+
77+
if (p != NULL) {
78+
/* Test one nested level. It should just work. */
79+
printk("trying interrupt handler\n");
80+
irq_offload(intr_handler, NULL);
81+
82+
k_sem_give((struct k_sem *)p);
3483
} else {
35-
printk("fatal error was unexpected, aborting\n");
36-
TC_END_REPORT(TC_FAIL);
37-
k_fatal_halt(reason);
84+
printk("interrupt handler nested\n");
3885
}
3986
}
4087

88+
void thread_b_entry(void *p1, void *p2, void *p3)
89+
{
90+
k_sem_take(&thread_b_sem, K_FOREVER);
91+
92+
irq_offload(intr_handler, &thread_b_irq_sem);
93+
94+
k_sem_take(&thread_b_irq_sem, K_FOREVER);
95+
}
96+
97+
ZTEST(cet, test_shstk_irq)
98+
{
99+
k_thread_start(thread_b);
100+
101+
k_sem_give(&thread_b_sem);
102+
103+
k_thread_join(thread_b, K_FOREVER);
104+
}
105+
106+
void thread_a_entry(void *p1, void *p2, void *p3)
107+
{
108+
k_sem_take(&thread_a_sem, K_FOREVER);
109+
110+
fail();
111+
112+
zassert_unreachable("should not reach here");
113+
}
114+
115+
ZTEST(cet, test_shstk)
116+
{
117+
k_thread_start(thread_a);
118+
119+
expect_fault = true;
120+
expect_code = CTRL_PROTECTION_ERRORCODE_NEAR_RET;
121+
expect_reason = IV_CTRL_PROTECTION_EXCEPTION;
122+
k_sem_give(&thread_a_sem);
123+
124+
k_sem_take(error_handler_sem, K_FOREVER);
125+
k_thread_abort(thread_a);
126+
}
127+
#endif /* CONFIG_HW_SHADOW_STACK */
128+
129+
#ifdef CONFIG_X86_CET_IBT
130+
extern int should_work(int a);
131+
extern int should_not_work(int a);
132+
41133
/* Round trip to trick optimisations and ensure the calls are indirect */
42134
int do_call(int (*func)(int), int a)
43135
{
@@ -50,8 +142,10 @@ ZTEST(cet, test_ibt)
50142

51143
expect_fault = true;
52144
expect_code = CTRL_PROTECTION_ERRORCODE_ENDBRANCH;
145+
expect_reason = IV_CTRL_PROTECTION_EXCEPTION;
53146
do_call(should_not_work, 1);
54147
zassert_unreachable("should_not_work did not fault");
55148
}
149+
#endif /* CONFIG_X86_CET_IBT */
56150

57151
ZTEST_SUITE(cet, NULL, NULL, NULL, NULL, NULL);

tests/arch/x86/cet/testcase.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,14 @@ tests:
1717
filter: CONFIG_X86_64
1818
extra_configs:
1919
- CONFIG_X86_CET_IBT=y
20+
arch.x86.cet.kernel.shstk64:
21+
arch_allow: x86
22+
filter: CONFIG_X86_64
23+
extra_configs:
24+
- CONFIG_HW_SHADOW_STACK=y
25+
arch.x86.cet.kernel.shstk32:
26+
arch_allow: x86
27+
filter: not CONFIG_X86_64
28+
extra_configs:
29+
- CONFIG_DEBUG_COREDUMP=y
30+
- CONFIG_HW_SHADOW_STACK=y

0 commit comments

Comments
 (0)