1534217f7SWarner Losh /*
2534217f7SWarner Losh * FreeBSD setup_initial_stack() implementation.
3534217f7SWarner Losh *
4534217f7SWarner Losh * Copyright (c) 2013-14 Stacey D. Son
5534217f7SWarner Losh *
6534217f7SWarner Losh * This program is free software; you can redistribute it and/or modify
7534217f7SWarner Losh * it under the terms of the GNU General Public License as published by
8534217f7SWarner Losh * the Free Software Foundation; either version 2 of the License, or
9534217f7SWarner Losh * (at your option) any later version.
10534217f7SWarner Losh *
11534217f7SWarner Losh * This program is distributed in the hope that it will be useful,
12534217f7SWarner Losh * but WITHOUT ANY WARRANTY; without even the implied warranty of
13534217f7SWarner Losh * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14534217f7SWarner Losh * GNU General Public License for more details.
15534217f7SWarner Losh *
16534217f7SWarner Losh * You should have received a copy of the GNU General Public License
17534217f7SWarner Losh * along with this program; if not, see <http://www.gnu.org/licenses/>.
18534217f7SWarner Losh */
19534217f7SWarner Losh
209c092804SMarkus Armbruster #ifndef TARGET_OS_STACK_H
219c092804SMarkus Armbruster #define TARGET_OS_STACK_H
22534217f7SWarner Losh
23534217f7SWarner Losh #include <sys/param.h>
24534217f7SWarner Losh #include "target_arch_sigtramp.h"
25534217f7SWarner Losh #include "qemu/guest-random.h"
26*82723866SPhilippe Mathieu-Daudé #include "user/tswap-target.h"
27534217f7SWarner Losh
28534217f7SWarner Losh /*
29944399ffSMichael Tokarev * The initial FreeBSD stack is as follows:
30534217f7SWarner Losh * (see kern/kern_exec.c exec_copyout_strings() )
31534217f7SWarner Losh *
32534217f7SWarner Losh * Hi Address -> char **ps_argvstr (struct ps_strings for ps, w, etc.)
33534217f7SWarner Losh * unsigned ps_nargvstr
34534217f7SWarner Losh * char **ps_envstr
35534217f7SWarner Losh * PS_STRINGS -> unsigned ps_nenvstr
36534217f7SWarner Losh *
37534217f7SWarner Losh * machine dependent sigcode (sv_sigcode of size
38534217f7SWarner Losh * sv_szsigcode)
39534217f7SWarner Losh *
40534217f7SWarner Losh * execpath (absolute image path for rtld)
41534217f7SWarner Losh *
42534217f7SWarner Losh * SSP Canary (sizeof(long) * 8)
43534217f7SWarner Losh *
44534217f7SWarner Losh * page sizes array (usually sizeof(u_long) )
45534217f7SWarner Losh *
46534217f7SWarner Losh * "destp" -> argv, env strings (up to 262144 bytes)
47534217f7SWarner Losh */
setup_initial_stack(struct bsd_binprm * bprm,abi_ulong * ret_addr,abi_ulong * stringp)48534217f7SWarner Losh static inline int setup_initial_stack(struct bsd_binprm *bprm,
49534217f7SWarner Losh abi_ulong *ret_addr, abi_ulong *stringp)
50534217f7SWarner Losh {
51534217f7SWarner Losh int i;
52534217f7SWarner Losh abi_ulong stack_hi_addr;
53534217f7SWarner Losh size_t execpath_len, stringspace;
54534217f7SWarner Losh abi_ulong destp, argvp, envp, p;
55534217f7SWarner Losh struct target_ps_strings ps_strs;
56534217f7SWarner Losh char canary[sizeof(abi_long) * 8];
57534217f7SWarner Losh
58534217f7SWarner Losh stack_hi_addr = p = target_stkbas + target_stksiz;
59534217f7SWarner Losh
60534217f7SWarner Losh /* Save some space for ps_strings. */
61534217f7SWarner Losh p -= sizeof(struct target_ps_strings);
62534217f7SWarner Losh
63944399ffSMichael Tokarev /* Add machine dependent sigcode. */
64534217f7SWarner Losh p -= TARGET_SZSIGCODE;
65534217f7SWarner Losh if (setup_sigtramp(p, (unsigned)offsetof(struct target_sigframe, sf_uc),
66534217f7SWarner Losh TARGET_FREEBSD_NR_sigreturn)) {
67534217f7SWarner Losh errno = EFAULT;
68534217f7SWarner Losh return -1;
69534217f7SWarner Losh }
70534217f7SWarner Losh if (bprm->fullpath) {
71534217f7SWarner Losh execpath_len = strlen(bprm->fullpath) + 1;
72534217f7SWarner Losh p -= roundup(execpath_len, sizeof(abi_ulong));
73534217f7SWarner Losh if (memcpy_to_target(p, bprm->fullpath, execpath_len)) {
74534217f7SWarner Losh errno = EFAULT;
75534217f7SWarner Losh return -1;
76534217f7SWarner Losh }
77534217f7SWarner Losh }
78534217f7SWarner Losh /* Add canary for SSP. */
79534217f7SWarner Losh qemu_guest_getrandom_nofail(canary, sizeof(canary));
80534217f7SWarner Losh p -= roundup(sizeof(canary), sizeof(abi_ulong));
81534217f7SWarner Losh if (memcpy_to_target(p, canary, sizeof(canary))) {
82534217f7SWarner Losh errno = EFAULT;
83534217f7SWarner Losh return -1;
84534217f7SWarner Losh }
85534217f7SWarner Losh /* Add page sizes array. */
86534217f7SWarner Losh p -= sizeof(abi_ulong);
87534217f7SWarner Losh if (put_user_ual(TARGET_PAGE_SIZE, p)) {
88534217f7SWarner Losh errno = EFAULT;
89534217f7SWarner Losh return -1;
90534217f7SWarner Losh }
91534217f7SWarner Losh /*
92534217f7SWarner Losh * Deviate from FreeBSD stack layout: force stack to new page here
93534217f7SWarner Losh * so that signal trampoline is not sharing the page with user stack
94534217f7SWarner Losh * frames. This is actively harmful in qemu as it marks pages with
95534217f7SWarner Losh * code it translated as read-only, which is somewhat problematic
96534217f7SWarner Losh * for user trying to use the stack as intended.
97534217f7SWarner Losh */
98534217f7SWarner Losh p = rounddown(p, TARGET_PAGE_SIZE);
99534217f7SWarner Losh
100534217f7SWarner Losh /* Calculate the string space needed */
101534217f7SWarner Losh stringspace = 0;
102534217f7SWarner Losh for (i = 0; i < bprm->argc; ++i) {
103534217f7SWarner Losh stringspace += strlen(bprm->argv[i]) + 1;
104534217f7SWarner Losh }
105534217f7SWarner Losh for (i = 0; i < bprm->envc; ++i) {
106534217f7SWarner Losh stringspace += strlen(bprm->envp[i]) + 1;
107534217f7SWarner Losh }
108534217f7SWarner Losh if (stringspace > TARGET_ARG_MAX) {
109534217f7SWarner Losh errno = ENOMEM;
110534217f7SWarner Losh return -1;
111534217f7SWarner Losh }
112534217f7SWarner Losh /* Make room for the argv and envp strings */
113534217f7SWarner Losh destp = rounddown(p - stringspace, sizeof(abi_ulong));
114534217f7SWarner Losh p = argvp = destp - (bprm->argc + bprm->envc + 2) * sizeof(abi_ulong);
115534217f7SWarner Losh /* Remember the strings pointer */
116534217f7SWarner Losh if (stringp) {
117534217f7SWarner Losh *stringp = destp;
118534217f7SWarner Losh }
119534217f7SWarner Losh /*
120534217f7SWarner Losh * Add argv strings. Note that the argv[] vectors are added by
121534217f7SWarner Losh * loader_build_argptr()
122534217f7SWarner Losh */
123534217f7SWarner Losh /* XXX need to make room for auxargs */
124534217f7SWarner Losh ps_strs.ps_argvstr = tswapl(argvp);
125534217f7SWarner Losh ps_strs.ps_nargvstr = tswap32(bprm->argc);
126534217f7SWarner Losh for (i = 0; i < bprm->argc; ++i) {
127534217f7SWarner Losh size_t len = strlen(bprm->argv[i]) + 1;
128534217f7SWarner Losh
129534217f7SWarner Losh if (memcpy_to_target(destp, bprm->argv[i], len)) {
130534217f7SWarner Losh errno = EFAULT;
131534217f7SWarner Losh return -1;
132534217f7SWarner Losh }
133534217f7SWarner Losh if (put_user_ual(destp, argvp)) {
134534217f7SWarner Losh errno = EFAULT;
135534217f7SWarner Losh return -1;
136534217f7SWarner Losh }
137534217f7SWarner Losh argvp += sizeof(abi_ulong);
138534217f7SWarner Losh destp += len;
139534217f7SWarner Losh }
140534217f7SWarner Losh if (put_user_ual(0, argvp)) {
141534217f7SWarner Losh errno = EFAULT;
142534217f7SWarner Losh return -1;
143534217f7SWarner Losh }
144534217f7SWarner Losh /*
145534217f7SWarner Losh * Add env strings. Note that the envp[] vectors are added by
146534217f7SWarner Losh * loader_build_argptr().
147534217f7SWarner Losh */
148534217f7SWarner Losh envp = argvp + sizeof(abi_ulong);
149534217f7SWarner Losh ps_strs.ps_envstr = tswapl(envp);
150534217f7SWarner Losh ps_strs.ps_nenvstr = tswap32(bprm->envc);
151534217f7SWarner Losh for (i = 0; i < bprm->envc; ++i) {
152534217f7SWarner Losh size_t len = strlen(bprm->envp[i]) + 1;
153534217f7SWarner Losh
154534217f7SWarner Losh if (memcpy_to_target(destp, bprm->envp[i], len)) {
155534217f7SWarner Losh errno = EFAULT;
156534217f7SWarner Losh return -1;
157534217f7SWarner Losh }
158534217f7SWarner Losh if (put_user_ual(destp, envp)) {
159534217f7SWarner Losh errno = EFAULT;
160534217f7SWarner Losh return -1;
161534217f7SWarner Losh }
162534217f7SWarner Losh envp += sizeof(abi_ulong);
163534217f7SWarner Losh destp += len;
164534217f7SWarner Losh }
165534217f7SWarner Losh if (put_user_ual(0, envp)) {
166534217f7SWarner Losh errno = EFAULT;
167534217f7SWarner Losh return -1;
168534217f7SWarner Losh }
169534217f7SWarner Losh if (memcpy_to_target(stack_hi_addr - sizeof(ps_strs), &ps_strs,
170534217f7SWarner Losh sizeof(ps_strs))) {
171534217f7SWarner Losh errno = EFAULT;
172534217f7SWarner Losh return -1;
173534217f7SWarner Losh }
174534217f7SWarner Losh
175534217f7SWarner Losh if (ret_addr) {
176534217f7SWarner Losh *ret_addr = p;
177534217f7SWarner Losh }
178534217f7SWarner Losh
179534217f7SWarner Losh return 0;
180534217f7SWarner Losh }
181534217f7SWarner Losh
1829c092804SMarkus Armbruster #endif /* TARGET_OS_STACK_H */
183