xref: /openbmc/qemu/bsd-user/freebsd/target_os_stack.h (revision fd87be1dada5672f877e03c2ca8504458292c479)
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