1 /* 2 * Copyright (c) 2015 Author: Oleg Nesterov <oleg (at) redhat.com> 3 * Modify: Li Wang <liwang (at) redhat.com> 4 * 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms of version 2 of the GNU General Public License as 7 * published by the Free Software Foundation. 8 * 9 * This program is distributed in the hope that it would be useful, but 10 * WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 * 13 * you should have received a copy of the GNU General Public License along 14 * with this program; if not, write the Free Software Foundation, Inc., 15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 16 */ 17 18 /* 19 * Description: 20 * 21 * save_xstate_sig()->drop_init_fpu() doesn't look right. setup_rt_frame() 22 * can fail after that, in this case the next setup_rt_frame() triggered 23 * by SIGSEGV won't save fpu simply because the old state was lost. This 24 * obviously mean that fpu won't be restored after sys_rt_sigreturn() from 25 * SIGSEGV handler. 26 * 27 * These commits fix the issue on v3.17-rc3-3 stable kernel: 28 * 29 * commit df24fb859a4e200d9324e2974229fbb7adf00aef 30 * Author: Oleg Nesterov <oleg (at) redhat.com> 31 * Date: Tue Sep 2 19:57:17 2014 +0200 32 * 33 * commit 66463db4fc5605d51c7bb81d009d5bf30a783a2c 34 * Author: Oleg Nesterov <oleg (at) redhat.com> 35 * Date: Tue Sep 2 19:57:13 2014 +0200 36 * 37 * Reproduce: 38 * Test-case (needs -O2). 39 */ 40 41 #include <stdio.h> 42 #include <signal.h> 43 #include <unistd.h> 44 #include <sys/syscall.h> 45 #include <sys/mman.h> 46 #include <pthread.h> 47 #include <assert.h> 48 #include <errno.h> 49 50 #include "test.h" 51 #include "linux_syscall_numbers.h" 52 53 char *TCID = "signal06"; 54 int TST_TOTAL = 5; 55 56 #if __x86_64__ 57 58 #define LOOPS 10000 59 60 volatile double D; 61 volatile int FLAGE; 62 63 char altstack[4096 * 10] __attribute__((aligned(4096))); 64 65 void test(double d) 66 { 67 int loop = 0; 68 int pid = getpid(); 69 70 D = d; 71 while (D == d && loop < LOOPS) { 72 /* sys_tkill(pid, SIGHUP); asm to avoid save/reload 73 * fp regs around c call */ 74 asm ("" : : "a"(__NR_tkill), "D"(pid), "S"(SIGHUP)); 75 asm ("syscall" : : : "ax"); 76 77 loop++; 78 } 79 80 FLAGE = 1; 81 tst_resm(TINFO, "loop = %d", loop); 82 83 if (loop == LOOPS) { 84 tst_resm(TPASS, "%s call succeeded", TCID); 85 } else { 86 tst_resm(TFAIL, "Bug Reproduced!"); 87 tst_exit(); 88 } 89 } 90 91 void sigh(int sig LTP_ATTRIBUTE_UNUSED) 92 { 93 } 94 95 void *tfunc(void *arg LTP_ATTRIBUTE_UNUSED) 96 { 97 for (; ;) { 98 TEST(mprotect(altstack, sizeof(altstack), PROT_READ)); 99 if (TEST_RETURN == -1) 100 tst_brkm(TBROK | TTERRNO, NULL, "mprotect failed"); 101 102 TEST(mprotect(altstack, sizeof(altstack), PROT_READ|PROT_WRITE)); 103 if (TEST_RETURN == -1) 104 tst_brkm(TBROK | TTERRNO, NULL, "mprotect failed"); 105 106 if (FLAGE == 1) 107 return NULL; 108 } 109 } 110 111 int main(int ac, char **av) 112 { 113 int i, lc; 114 pthread_t pt; 115 116 tst_parse_opts(ac, av, NULL, NULL); 117 118 stack_t st = { 119 .ss_sp = altstack, 120 .ss_size = sizeof(altstack), 121 .ss_flags = SS_ONSTACK, 122 }; 123 124 struct sigaction sa = { 125 .sa_handler = sigh, 126 }; 127 128 TEST(sigaction(SIGSEGV, &sa, NULL)); 129 if (TEST_RETURN == -1) 130 tst_brkm(TBROK | TTERRNO, NULL, 131 "SIGSEGV signal setup failed"); 132 sigaltstack(&st, NULL); 133 sa.sa_flags = SA_ONSTACK; 134 135 TEST(sigaction(SIGHUP, &sa, NULL)); 136 if (TEST_RETURN == -1) 137 tst_brkm(TBROK | TTERRNO, NULL, 138 "SIGHUP signal setup failed"); 139 140 for (lc = 0; TEST_LOOPING(lc); lc++) { 141 tst_count = 0; 142 143 for (i = 0; i < TST_TOTAL; i++) { 144 FLAGE = 0; 145 146 TEST(pthread_create(&pt, NULL, tfunc, NULL)); 147 if (TEST_RETURN) 148 tst_brkm(TBROK | TRERRNO, NULL, 149 "pthread_create failed"); 150 151 test(123.456); 152 153 TEST(pthread_join(pt, NULL)); 154 if (TEST_RETURN) 155 tst_brkm(TBROK | TRERRNO, NULL, 156 "pthread_join failed"); 157 } 158 } 159 160 tst_exit(); 161 } 162 163 #else 164 int main(void) 165 { 166 tst_brkm(TCONF, NULL, "Only test on x86_64."); 167 } 168 #endif 169