xref: /qemu/bsd-user/freebsd/target_os_stack.h (revision 727385c4)
1 /*
2  *  FreeBSD setup_initial_stack() implementation.
3  *
4  *  Copyright (c) 2013-14 Stacey D. Son
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifndef _TARGET_OS_STACK_H_
21 #define _TARGET_OS_STACK_H_
22 
23 #include <sys/param.h>
24 #include "target_arch_sigtramp.h"
25 #include "qemu/guest-random.h"
26 
27 /*
28  * The inital FreeBSD stack is as follows:
29  * (see kern/kern_exec.c exec_copyout_strings() )
30  *
31  *  Hi Address -> char **ps_argvstr  (struct ps_strings for ps, w, etc.)
32  *                unsigned ps_nargvstr
33  *                char **ps_envstr
34  *  PS_STRINGS -> unsigned ps_nenvstr
35  *
36  *                machine dependent sigcode (sv_sigcode of size
37  *                                           sv_szsigcode)
38  *
39  *                execpath          (absolute image path for rtld)
40  *
41  *                SSP Canary        (sizeof(long) * 8)
42  *
43  *                page sizes array  (usually sizeof(u_long) )
44  *
45  *  "destp" ->    argv, env strings (up to 262144 bytes)
46  */
47 static inline int setup_initial_stack(struct bsd_binprm *bprm,
48         abi_ulong *ret_addr, abi_ulong *stringp)
49 {
50     int i;
51     abi_ulong stack_hi_addr;
52     size_t execpath_len, stringspace;
53     abi_ulong destp, argvp, envp, p;
54     struct target_ps_strings ps_strs;
55     char canary[sizeof(abi_long) * 8];
56 
57     stack_hi_addr = p = target_stkbas + target_stksiz;
58 
59     /* Save some space for ps_strings. */
60     p -= sizeof(struct target_ps_strings);
61 
62     /* Add machine depedent sigcode. */
63     p -= TARGET_SZSIGCODE;
64     if (setup_sigtramp(p, (unsigned)offsetof(struct target_sigframe, sf_uc),
65             TARGET_FREEBSD_NR_sigreturn)) {
66         errno = EFAULT;
67         return -1;
68     }
69     if (bprm->fullpath) {
70         execpath_len = strlen(bprm->fullpath) + 1;
71         p -= roundup(execpath_len, sizeof(abi_ulong));
72         if (memcpy_to_target(p, bprm->fullpath, execpath_len)) {
73             errno = EFAULT;
74             return -1;
75         }
76     }
77     /* Add canary for SSP. */
78     qemu_guest_getrandom_nofail(canary, sizeof(canary));
79     p -= roundup(sizeof(canary), sizeof(abi_ulong));
80     if (memcpy_to_target(p, canary, sizeof(canary))) {
81         errno = EFAULT;
82         return -1;
83     }
84     /* Add page sizes array. */
85     p -= sizeof(abi_ulong);
86     if (put_user_ual(TARGET_PAGE_SIZE, p)) {
87         errno = EFAULT;
88         return -1;
89     }
90     /*
91      * Deviate from FreeBSD stack layout: force stack to new page here
92      * so that signal trampoline is not sharing the page with user stack
93      * frames. This is actively harmful in qemu as it marks pages with
94      * code it translated as read-only, which is somewhat problematic
95      * for user trying to use the stack as intended.
96      */
97     p = rounddown(p, TARGET_PAGE_SIZE);
98 
99     /* Calculate the string space needed */
100     stringspace = 0;
101     for (i = 0; i < bprm->argc; ++i) {
102         stringspace += strlen(bprm->argv[i]) + 1;
103     }
104     for (i = 0; i < bprm->envc; ++i) {
105         stringspace += strlen(bprm->envp[i]) + 1;
106     }
107     if (stringspace > TARGET_ARG_MAX) {
108         errno = ENOMEM;
109         return -1;
110     }
111     /* Make room for the argv and envp strings */
112     destp = rounddown(p - stringspace, sizeof(abi_ulong));
113     p = argvp = destp - (bprm->argc + bprm->envc + 2) * sizeof(abi_ulong);
114     /* Remember the strings pointer */
115     if (stringp) {
116         *stringp = destp;
117     }
118     /*
119      * Add argv strings.  Note that the argv[] vectors are added by
120      * loader_build_argptr()
121      */
122     /* XXX need to make room for auxargs */
123     ps_strs.ps_argvstr = tswapl(argvp);
124     ps_strs.ps_nargvstr = tswap32(bprm->argc);
125     for (i = 0; i < bprm->argc; ++i) {
126         size_t len = strlen(bprm->argv[i]) + 1;
127 
128         if (memcpy_to_target(destp, bprm->argv[i], len)) {
129             errno = EFAULT;
130             return -1;
131         }
132         if (put_user_ual(destp, argvp)) {
133             errno = EFAULT;
134             return -1;
135         }
136         argvp += sizeof(abi_ulong);
137         destp += len;
138     }
139     if (put_user_ual(0, argvp)) {
140         errno = EFAULT;
141         return -1;
142     }
143     /*
144      * Add env strings. Note that the envp[] vectors are added by
145      * loader_build_argptr().
146      */
147     envp = argvp + sizeof(abi_ulong);
148     ps_strs.ps_envstr = tswapl(envp);
149     ps_strs.ps_nenvstr = tswap32(bprm->envc);
150     for (i = 0; i < bprm->envc; ++i) {
151         size_t len = strlen(bprm->envp[i]) + 1;
152 
153         if (memcpy_to_target(destp, bprm->envp[i], len)) {
154             errno = EFAULT;
155             return -1;
156         }
157         if (put_user_ual(destp, envp)) {
158             errno = EFAULT;
159             return -1;
160         }
161         envp += sizeof(abi_ulong);
162         destp += len;
163     }
164     if (put_user_ual(0, envp)) {
165         errno = EFAULT;
166         return -1;
167     }
168     if (memcpy_to_target(stack_hi_addr - sizeof(ps_strs), &ps_strs,
169                 sizeof(ps_strs))) {
170         errno = EFAULT;
171         return -1;
172     }
173 
174     if (ret_addr) {
175         *ret_addr = p;
176     }
177 
178     return 0;
179  }
180 
181 #endif /* !_TARGET_OS_STACK_H_ */
182