Home | History | Annotate | Line # | Download | only in kernel
      1 /*	$NetBSD: getstack.c,v 1.1 2026/08/28 06:35:26 riastradh Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2026 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26  * POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 __RCSID("$NetBSD: getstack.c,v 1.1 2026/08/28 06:35:26 riastradh Exp $");
     31 
     32 #include "getstack.h"
     33 
     34 #include <sys/exec.h>
     35 #include <sys/exec_elf.h>
     36 #include <sys/resource.h>
     37 #include <sys/sysctl.h>
     38 #include <sys/types.h>
     39 
     40 #include <dlfcn.h>
     41 #include <err.h>
     42 #include <pthread.h>
     43 #include <signal.h>
     44 #include <ucontext.h>
     45 #include <unistd.h>
     46 
     47 extern struct ps_strings *__ps_strings; /* XXX */
     48 
     49 void *
     50 getstack_elf_auxv(void)
     51 {
     52 	const AuxInfo *aux;
     53 
     54 	for (aux = _dlauxinfo(); aux->a_type != AT_NULL; aux++) {
     55 		if (aux->a_type == AT_STACKBASE)
     56 			return (void *)aux->a_v;
     57 	}
     58 	warnx("missing AT_STACKBASE in ELF auxv");
     59 	return NULL;
     60 }
     61 
     62 void *
     63 getstack_getcontext(size_t *usedsizep)
     64 {
     65 	ucontext_t uc;
     66 
     67 	if (getcontext(&uc) == -1) {
     68 		warn("getcontext");
     69 		*usedsizep = (size_t)-1;
     70 		return NULL;
     71 	}
     72 	*usedsizep = uc.uc_stack.ss_size;
     73 	return uc.uc_stack.ss_sp;
     74 }
     75 
     76 void *
     77 getstack_psstrings(void)
     78 {
     79 
     80 	if (__ps_strings == NULL) {
     81 		warnx("missing __ps_strings");
     82 		return NULL;
     83 	}
     84 
     85 #ifdef __MACHINE_STACK_GROWS_UP
     86 	return __ps_strings;
     87 #else
     88 	return (char *)__ps_strings + sizeof(*__ps_strings);
     89 #endif
     90 }
     91 
     92 void *
     93 getstack_pthreadattr(size_t *minsizep)
     94 {
     95 	pthread_attr_t attr_storage, *attr = NULL;
     96 	void *addr = NULL;
     97 	int error;
     98 
     99 	error = pthread_getattr_np(pthread_self(), &attr_storage);
    100 	if (error) {
    101 		warnc(error, "pthread_getattr_np");
    102 		addr = NULL;
    103 		*minsizep = (size_t)-1;
    104 		goto out;
    105 	}
    106 	attr = &attr_storage;
    107 
    108 	error = pthread_attr_getstack(attr, &addr, minsizep);
    109 	if (error) {
    110 		warnc(error, "pthread_attr_getstack");
    111 		addr = NULL;
    112 		*minsizep = (size_t)-1;
    113 		goto out;
    114 	}
    115 
    116 #ifndef __MACHINE_STACK_GROWS_UP
    117 	addr = (char *)addr + *minsizep;
    118 #endif
    119 
    120 out:	error = pthread_attr_destroy(attr);
    121 	if (error)
    122 		warnc(error, "pthread_attr_destroy");
    123 	return addr;
    124 }
    125 
    126 void
    127 getstack_rlimit(size_t *minsizep, size_t *maxsizep)
    128 {
    129 	struct rlimit rlim;
    130 
    131 	if (getrlimit(RLIMIT_STACK, &rlim) == -1) {
    132 		warn("getrlimit(RLIMIT_STACK)");
    133 		*minsizep = (size_t)-1;
    134 		*maxsizep = (size_t)-1;
    135 		return;
    136 	}
    137 	*minsizep = rlim.rlim_cur;
    138 	*maxsizep = rlim.rlim_max;
    139 }
    140 
    141 void *
    142 getstack_sigaltstack(size_t *minsizep)
    143 {
    144 	stack_t ss;
    145 
    146 	if (sigaltstack(NULL, &ss) == -1) {
    147 		warn("sigaltstack");
    148 		*minsizep = (size_t)-1;
    149 		return NULL;
    150 	}
    151 	*minsizep = ss.ss_size;
    152 	return ss.ss_sp;
    153 }
    154 
    155 int
    156 checkgetstack(void)
    157 {
    158 	void *addr_elf_auxv;
    159 	void *addr_getcontext;
    160 	void *addr_psstrings;
    161 	void *addr_pthreadattr;
    162 	void *addr_sigaltstack;
    163 	size_t usedsize_getcontext;
    164 	size_t minsize_pthreadattr;
    165 	size_t minsize_rlimit;
    166 	size_t maxsize_rlimit;
    167 	size_t minsize_sigaltstack;
    168 
    169 	addr_elf_auxv = getstack_elf_auxv();
    170 	addr_getcontext = getstack_getcontext(&usedsize_getcontext);
    171 	addr_psstrings = getstack_psstrings();
    172 	addr_pthreadattr = getstack_pthreadattr(&minsize_pthreadattr);
    173 	getstack_rlimit(&minsize_rlimit, &maxsize_rlimit);
    174 	addr_sigaltstack = getstack_sigaltstack(&minsize_sigaltstack);
    175 
    176 	warnx("%16s: addr=%p", "ELF AT_STACKBASE", addr_elf_auxv);
    177 	warnx("%16s: addr=%-16p used=%zx", "getcontext",
    178 	    addr_getcontext, usedsize_getcontext);
    179 	warnx("%16s: addr=%p", "__ps_strings", addr_psstrings);
    180 	warnx("%16s: addr=%-16p size=%zx", "pthread attr",
    181 	    addr_pthreadattr, minsize_pthreadattr);
    182 	warnx("%16s:      %-16s size=%zx min, %zx max", "RLIMIT_STACK",
    183 	    "", minsize_rlimit, maxsize_rlimit);
    184 	warnx("%16s: addr=%-16p size=%zx", "sigaltstack",
    185 	    addr_sigaltstack, minsize_sigaltstack);
    186 
    187 	if (addr_elf_auxv == addr_getcontext &&
    188 	    addr_elf_auxv == addr_psstrings &&
    189 	    addr_elf_auxv == addr_pthreadattr &&
    190 	    (addr_sigaltstack == NULL || addr_elf_auxv == addr_sigaltstack) &&
    191 	    usedsize_getcontext <= minsize_rlimit &&
    192 	    minsize_pthreadattr == minsize_rlimit &&
    193 	    (addr_sigaltstack == NULL ||
    194 		minsize_pthreadattr == minsize_sigaltstack) &&
    195 	    minsize_rlimit <= maxsize_rlimit) {
    196 		warnx("Stack OK");
    197 		return 0;
    198 	}
    199 
    200 	warnx("Stack disagreement!");
    201 	return -1;
    202 }
    203 
    204 int
    205 checkgetstack_singlethreaded(void)
    206 {
    207 	void *addr_elf_auxv;
    208 	void *addr_getcontext;
    209 	void *addr_psstrings;
    210 	void *addr_sigaltstack;
    211 	size_t usedsize_getcontext;
    212 	size_t minsize_rlimit;
    213 	size_t maxsize_rlimit;
    214 	size_t minsize_sigaltstack;
    215 
    216 	addr_elf_auxv = getstack_elf_auxv();
    217 	addr_getcontext = getstack_getcontext(&usedsize_getcontext);
    218 	addr_psstrings = getstack_psstrings();
    219 	getstack_rlimit(&minsize_rlimit, &maxsize_rlimit);
    220 	addr_sigaltstack = getstack_sigaltstack(&minsize_sigaltstack);
    221 
    222 	warnx("%16s: addr=%p", "ELF AT_STACKBASE", addr_elf_auxv);
    223 	warnx("%16s: addr=%-16p used=%zx", "getcontext",
    224 	    addr_getcontext, usedsize_getcontext);
    225 	warnx("%16s: addr=%p", "__ps_strings", addr_psstrings);
    226 	warnx("%16s:      %-16s size=%zx min, %zx max", "RLIMIT_STACK",
    227 	    "", minsize_rlimit, maxsize_rlimit);
    228 	warnx("%16s: addr=%-16p size=%zx", "sigaltstack",
    229 	    addr_sigaltstack, minsize_sigaltstack);
    230 
    231 	if (addr_elf_auxv == addr_getcontext &&
    232 	    addr_elf_auxv == addr_psstrings &&
    233 	    (addr_sigaltstack == NULL || addr_elf_auxv == addr_sigaltstack) &&
    234 	    usedsize_getcontext <= minsize_rlimit &&
    235 	    (addr_sigaltstack == NULL ||
    236 		minsize_rlimit == minsize_sigaltstack) &&
    237 	    minsize_rlimit <= maxsize_rlimit) {
    238 		warnx("Stack OK");
    239 		return 0;
    240 	}
    241 
    242 	warnx("Stack disagreement!");
    243 	return -1;
    244 }
    245