Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

kselftest/arm64: Exit streaming mode after collecting signal context

When we collect a signal context with one of the SME modes enabled we will
have enabled that mode behind the compiler and libc's back so they may
issue some instructions not valid in streaming mode, causing spurious
failures.

For the code prior to issuing the BRK to trigger signal handling we need to
stay in streaming mode if we were already there since that's a part of the
signal context the caller is trying to collect. Unfortunately this code
includes a memset() which is likely to be heavily optimised and is likely
to use FP instructions incompatible with streaming mode. We can avoid this
happening by open coding the memset(), inserting a volatile assembly
statement to avoid the compiler recognising what's being done and doing
something in optimisation. This code is not performance critical so the
inefficiency should not be an issue.

After collecting the context we can simply exit streaming mode, avoiding
these issues. Use a full SMSTOP for safety to prevent any issues appearing
with ZA.

Reported-by: Will Deacon <will@kernel.org>
Signed-off-by: Mark Brown <broonie@kernel.org>
Link: https://lore.kernel.org/r/20230728-arm64-signal-memcpy-fix-v4-1-0c1290db5d46@kernel.org
Signed-off-by: Will Deacon <will@kernel.org>

authored by

Mark Brown and committed by
Will Deacon
d6da04b6 d1890517

+24 -1
+24 -1
tools/testing/selftests/arm64/signal/test_signals_utils.h
··· 60 60 size_t dest_sz) 61 61 { 62 62 static volatile bool seen_already; 63 + int i; 64 + char *uc = (char *)dest_uc; 63 65 64 66 assert(td && dest_uc); 65 67 /* it's a genuine invocation..reinit */ 66 68 seen_already = 0; 67 69 td->live_uc_valid = 0; 68 70 td->live_sz = dest_sz; 69 - memset(dest_uc, 0x00, td->live_sz); 71 + 72 + /* 73 + * This is a memset() but we don't want the compiler to 74 + * optimise it into either instructions or a library call 75 + * which might be incompatible with streaming mode. 76 + */ 77 + for (i = 0; i < td->live_sz; i++) { 78 + uc[i] = 0; 79 + __asm__ ("" : "=r" (uc[i]) : "0" (uc[i])); 80 + } 81 + 70 82 td->live_uc = dest_uc; 71 83 /* 72 84 * Grab ucontext_t triggering a SIGTRAP. ··· 114 102 : "+m" (*dest_uc) 115 103 : 116 104 : "memory"); 105 + 106 + /* 107 + * If we were grabbing a streaming mode context then we may 108 + * have entered streaming mode behind the system's back and 109 + * libc or compiler generated code might decide to do 110 + * something invalid in streaming mode, or potentially even 111 + * the state of ZA. Issue a SMSTOP to exit both now we have 112 + * grabbed the state. 113 + */ 114 + if (td->feats_supported & FEAT_SME) 115 + asm volatile("msr S0_3_C4_C6_3, xzr"); 117 116 118 117 /* 119 118 * If we get here with seen_already==1 it implies the td->live_uc