// Program that loops for ever doing lots of recursions and system calls, // intended to be used as part of a stress test for GCS context switching. // // Copyright 2015-2023 Arm Ltd #include #define sa_sz 32 #define sa_flags 8 #define sa_handler 0 #define sa_mask_sz 8 #define si_code 8 #define SIGINT 2 #define SIGABRT 6 #define SIGUSR1 10 #define SIGSEGV 11 #define SIGUSR2 12 #define SIGTERM 15 #define SEGV_CPERR 10 #define SA_NODEFER 1073741824 #define SA_SIGINFO 4 #define ucontext_regs 184 #define PR_SET_SHADOW_STACK_STATUS 75 # define PR_SHADOW_STACK_ENABLE (1UL << 0) #define GCSPR_EL0 S3_3_C2_C5_1 .macro function name .macro endfunction .type \name, @function .purgem endfunction .endm \name: .endm // Print a single character x0 to stdout // Clobbers x0-x2,x8 function putc str x0, [sp, #-16]! mov x0, #1 // STDOUT_FILENO mov x1, sp mov x2, #1 mov x8, #__NR_write svc #0 add sp, sp, #16 ret endfunction .globl putc // Print a NUL-terminated string starting at address x0 to stdout // Clobbers x0-x3,x8 function puts mov x1, x0 mov x2, #0 0: ldrb w3, [x0], #1 cbz w3, 1f add x2, x2, #1 b 0b 1: mov w0, #1 // STDOUT_FILENO mov x8, #__NR_write svc #0 ret endfunction .globl puts // Utility macro to print a literal string // Clobbers x0-x4,x8 .macro puts string .pushsection .rodata.str1.1, "aMS", @progbits, 1 .L__puts_literal\@: .string "\string" .popsection ldr x0, =.L__puts_literal\@ bl puts .endm // Print an unsigned decimal number x0 to stdout // Clobbers x0-x4,x8 function putdec mov x1, sp str x30, [sp, #-32]! // Result can't be > 20 digits mov x2, #0 strb w2, [x1, #-1]! // Write the NUL terminator mov x2, #10 0: udiv x3, x0, x2 // div-mod loop to generate the digits msub x0, x3, x2, x0 add w0, w0, #'0' strb w0, [x1, #-1]! mov x0, x3 cbnz x3, 0b ldrb w0, [x1] cbnz w0, 1f mov w0, #'0' // Print "0" for 0, not "" strb w0, [x1, #-1]! 1: mov x0, x1 bl puts ldr x30, [sp], #32 ret endfunction .globl putdec // Print an unsigned decimal number x0 to stdout, followed by a newline // Clobbers x0-x5,x8 function putdecn mov x5, x30 bl putdec mov x0, #'\n' bl putc ret x5 endfunction .globl putdecn // Fill x1 bytes starting at x0 with 0. // Clobbers x1, x2. function memclr mov w2, #0 endfunction .globl memclr // fall through to memfill // Trivial memory fill: fill x1 bytes starting at address x0 with byte w2 // Clobbers x1 function memfill cmp x1, #0 b.eq 1f 0: strb w2, [x0], #1 subs x1, x1, #1 b.ne 0b 1: ret endfunction .globl memfill // w0: signal number // x1: sa_action // w2: sa_flags // Clobbers x0-x6,x8 function setsignal str x30, [sp, #-((sa_sz + 15) / 16 * 16 + 16)]! mov w4, w0 mov x5, x1 mov w6, w2 add x0, sp, #16 mov x1, #sa_sz bl memclr mov w0, w4 add x1, sp, #16 str w6, [x1, #sa_flags] str x5, [x1, #sa_handler] mov x2, #0 mov x3, #sa_mask_sz mov x8, #__NR_rt_sigaction svc #0 cbz w0, 1f puts "sigaction failure\n" b abort 1: ldr x30, [sp], #((sa_sz + 15) / 16 * 16 + 16) ret endfunction function tickle_handler // Perhaps collect GCSPR_EL0 here in future? ret endfunction function terminate_handler mov w21, w0 mov x20, x2 puts "Terminated by signal " mov w0, w21 bl putdec puts ", no error\n" mov x0, #0 mov x8, #__NR_exit svc #0 endfunction function segv_handler // stash the siginfo_t * mov x20, x1 // Disable GCS, we don't want additional faults logging things mov x0, PR_SET_SHADOW_STACK_STATUS mov x1, xzr mov x2, xzr mov x3, xzr mov x4, xzr mov x5, xzr mov x8, #__NR_prctl svc #0 puts "Got SIGSEGV code " ldr x21, [x20, #si_code] mov x0, x21 bl putdec // GCS faults should have si_code SEGV_CPERR cmp x21, #SEGV_CPERR bne 1f puts " (GCS violation)" 1: mov x0, '\n' bl putc b abort endfunction // Recurse x20 times .macro recurse id function recurse\id stp x29, x30, [sp, #-16]! mov x29, sp cmp x20, 0 beq 1f sub x20, x20, 1 bl recurse\id 1: ldp x29, x30, [sp], #16 // Do a syscall immediately prior to returning to try to provoke // scheduling and migration at a point where coherency issues // might trigger. mov x8, #__NR_getpid svc #0 ret endfunction .endm // Generate and use two copies so we're changing the GCS contents recurse 1 recurse 2 .globl _start function _start // Run with GCS mov x0, PR_SET_SHADOW_STACK_STATUS mov x1, PR_SHADOW_STACK_ENABLE mov x2, xzr mov x3, xzr mov x4, xzr mov x5, xzr mov x8, #__NR_prctl svc #0 cbz x0, 1f puts "Failed to enable GCS\n" b abort 1: mov w0, #SIGTERM adr x1, terminate_handler mov w2, #SA_SIGINFO bl setsignal mov w0, #SIGUSR1 adr x1, tickle_handler mov w2, #SA_SIGINFO orr w2, w2, #SA_NODEFER bl setsignal mov w0, #SIGSEGV adr x1, segv_handler mov w2, #SA_SIGINFO orr w2, w2, #SA_NODEFER bl setsignal puts "Running\n" loop: // Small recursion depth so we're frequently flipping between // the two recursors and changing what's on the stack mov x20, #5 bl recurse1 mov x20, #5 bl recurse2 b loop endfunction abort: mov x0, #255 mov x8, #__NR_exit svc #0