1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * Copyright (c) 2016 by Delphix. All rights reserved. 26 */ 27 28 #include "misc.h" 29 #include <ucontext.h> 30 #include <sys/frame.h> 31 #include <sys/stack.h> 32 #include <stdio.h> 33 34 #if defined(__sparc) || defined(__sparcv9) 35 extern void flush_windows(void); 36 #define UMEM_FRAMESIZE MINFRAME 37 38 #elif defined(__i386) || defined(__amd64) 39 /* 40 * On x86, MINFRAME is defined to be 0, but we want to be sure we can 41 * dereference the entire frame structure. 42 */ 43 #define UMEM_FRAMESIZE (sizeof (struct frame)) 44 45 #else 46 #error needs update for new architecture 47 #endif 48 49 /* 50 * Get a pc-only stacktrace. Used for kmem_alloc() buffer ownership tracking. 51 * Returns MIN(current stack depth, pcstack_limit). 52 */ 53 /*ARGSUSED*/ 54 int 55 getpcstack(uintptr_t *pcstack, int pcstack_limit, int check_signal) 56 { 57 struct frame *fp; 58 struct frame *nextfp, *minfp; 59 int depth = 0; 60 uintptr_t base = 0; 61 size_t size = 0; 62 #ifndef UMEM_STANDALONE 63 int on_altstack = 0; 64 uintptr_t sigbase = 0; 65 size_t sigsize = 0; 66 67 stack_t st; 68 69 if (stack_getbounds(&st) != 0) { 70 if (thr_stksegment(&st) != 0 || 71 (uintptr_t)st.ss_sp < st.ss_size) { 72 return (0); /* unable to get stack bounds */ 73 } 74 /* 75 * thr_stksegment(3C) has a slightly different interface than 76 * stack_getbounds(3C) -- correct it 77 */ 78 st.ss_sp = (void *)(((uintptr_t)st.ss_sp) - st.ss_size); 79 st.ss_flags = 0; /* can't be on-stack */ 80 } 81 on_altstack = (st.ss_flags & SS_ONSTACK); 82 83 if (st.ss_size != 0) { 84 base = (uintptr_t)st.ss_sp; 85 size = st.ss_size; 86 } else { 87 /* 88 * If size == 0, then ss_sp is the *top* of the stack. 89 * 90 * Since we only allow increasing frame pointers, and we 91 * know our caller set its up correctly, we can treat ss_sp 92 * as an upper bound safely. 93 */ 94 base = 0; 95 size = (uintptr_t)st.ss_sp; 96 } 97 98 if (check_signal != 0) { 99 void (*sigfunc)() = NULL; 100 int sigfuncsize = 0; 101 extern void thr_sighndlrinfo(void (**)(), int *); 102 103 thr_sighndlrinfo(&sigfunc, &sigfuncsize); 104 sigbase = (uintptr_t)sigfunc; 105 sigsize = sigfuncsize; 106 } 107 #else /* UMEM_STANDALONE */ 108 base = (uintptr_t)umem_min_stack; 109 size = umem_max_stack - umem_min_stack; 110 #endif 111 112 /* 113 * shorten size so that fr_savfp and fr_savpc will be within the stack 114 * bounds. 115 */ 116 if (size >= UMEM_FRAMESIZE - 1) 117 size -= (UMEM_FRAMESIZE - 1); 118 else 119 size = 0; 120 121 #if defined(__sparc) || defined(__sparcv9) 122 flush_windows(); 123 #endif 124 125 /* LINTED alignment */ 126 fp = (struct frame *)((caddr_t)getfp() + STACK_BIAS); 127 128 minfp = fp; 129 130 if (((uintptr_t)fp - base) >= size) 131 return (0); /* the frame pointer isn't in our stack */ 132 133 while (depth < pcstack_limit) { 134 uintptr_t tmp; 135 136 /* LINTED alignment */ 137 nextfp = (struct frame *)((caddr_t)fp->fr_savfp + STACK_BIAS); 138 tmp = (uintptr_t)nextfp; 139 140 /* 141 * Check nextfp for validity. It must be properly aligned, 142 * increasing compared to the last %fp (or the top of the 143 * stack we just switched to), and it must be inside 144 * [base, base + size). 145 */ 146 if (tmp != SA(tmp)) 147 break; 148 else if (nextfp <= minfp || (tmp - base) >= size) { 149 #ifndef UMEM_STANDALONE 150 if (tmp == (uintptr_t)NULL || !on_altstack) 151 break; 152 /* 153 * If we're on an alternate signal stack, try jumping 154 * to the main thread stack. 155 * 156 * If the main thread stack has an unlimited size, we 157 * punt, since we don't know where the frame pointer's 158 * been. 159 * 160 * (thr_stksegment() returns the *top of stack* 161 * in ss_sp, not the bottom) 162 */ 163 if (thr_stksegment(&st) == 0) { 164 if (st.ss_size >= (uintptr_t)st.ss_sp || 165 st.ss_size < UMEM_FRAMESIZE - 1) 166 break; 167 168 on_altstack = 0; 169 base = (uintptr_t)st.ss_sp - st.ss_size; 170 size = st.ss_size - (UMEM_FRAMESIZE - 1); 171 minfp = (struct frame *)base; 172 continue; /* try again */ 173 } 174 #endif 175 break; 176 } 177 178 #ifndef UMEM_STANDALONE 179 if (check_signal && (fp->fr_savpc - sigbase) <= sigsize) 180 umem_panic("called from signal handler"); 181 #endif 182 pcstack[depth++] = fp->fr_savpc; 183 fp = nextfp; 184 minfp = fp; 185 } 186 return (depth); 187 } 188