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 #include "user/tswap-target.h"
27 
28 /*
29  * The initial FreeBSD stack is as follows:
30  * (see kern/kern_exec.c exec_copyout_strings() )
31  *
32  *  Hi Address -> char **ps_argvstr  (struct ps_strings for ps, w, etc.)
33  *                unsigned ps_nargvstr
34  *                char **ps_envstr
35  *  PS_STRINGS -> unsigned ps_nenvstr
36  *
37  *                machine dependent sigcode (sv_sigcode of size
38  *                                           sv_szsigcode)
39  *
40  *                execpath          (absolute image path for rtld)
41  *
42  *                SSP Canary        (sizeof(long) * 8)
43  *
44  *                page sizes array  (usually sizeof(u_long) )
45  *
46  *  "destp" ->    argv, env strings (up to 262144 bytes)
47  */
48 static inline int setup_initial_stack(struct bsd_binprm *bprm,
49         abi_ulong *ret_addr, abi_ulong *stringp)
50 {
51     int i;
52     abi_ulong stack_hi_addr;
53     size_t execpath_len, stringspace;
54     abi_ulong destp, argvp, envp, p;
55     struct target_ps_strings ps_strs;
56     char canary[sizeof(abi_long) * 8];
57 
58     stack_hi_addr = p = target_stkbas + target_stksiz;
59 
60     /* Save some space for ps_strings. */
61     p -= sizeof(struct target_ps_strings);
62 
63     /* Add machine dependent sigcode. */
64     p -= TARGET_SZSIGCODE;
65     if (setup_sigtramp(p, (unsigned)offsetof(struct target_sigframe, sf_uc),
66             TARGET_FREEBSD_NR_sigreturn)) {
67         errno = EFAULT;
68         return -1;
69     }
70     if (bprm->fullpath) {
71         execpath_len = strlen(bprm->fullpath) + 1;
72         p -= roundup(execpath_len, sizeof(abi_ulong));
73         if (memcpy_to_target(p, bprm->fullpath, execpath_len)) {
74             errno = EFAULT;
75             return -1;
76         }
77     }
78     /* Add canary for SSP. */
79     qemu_guest_getrandom_nofail(canary, sizeof(canary));
80     p -= roundup(sizeof(canary), sizeof(abi_ulong));
81     if (memcpy_to_target(p, canary, sizeof(canary))) {
82         errno = EFAULT;
83         return -1;
84     }
85     /* Add page sizes array. */
86     p -= sizeof(abi_ulong);
87     if (put_user_ual(TARGET_PAGE_SIZE, p)) {
88         errno = EFAULT;
89         return -1;
90     }
91     /*
92      * Deviate from FreeBSD stack layout: force stack to new page here
93      * so that signal trampoline is not sharing the page with user stack
94      * frames. This is actively harmful in qemu as it marks pages with
95      * code it translated as read-only, which is somewhat problematic
96      * for user trying to use the stack as intended.
97      */
98     p = rounddown(p, TARGET_PAGE_SIZE);
99 
100     /* Calculate the string space needed */
101     stringspace = 0;
102     for (i = 0; i < bprm->argc; ++i) {
103         stringspace += strlen(bprm->argv[i]) + 1;
104     }
105     for (i = 0; i < bprm->envc; ++i) {
106         stringspace += strlen(bprm->envp[i]) + 1;
107     }
108     if (stringspace > TARGET_ARG_MAX) {
109         errno = ENOMEM;
110         return -1;
111     }
112     /* Make room for the argv and envp strings */
113     destp = rounddown(p - stringspace, sizeof(abi_ulong));
114     p = argvp = destp - (bprm->argc + bprm->envc + 2) * sizeof(abi_ulong);
115     /* Remember the strings pointer */
116     if (stringp) {
117         *stringp = destp;
118     }
119     /*
120      * Add argv strings.  Note that the argv[] vectors are added by
121      * loader_build_argptr()
122      */
123     /* XXX need to make room for auxargs */
124     ps_strs.ps_argvstr = tswapl(argvp);
125     ps_strs.ps_nargvstr = tswap32(bprm->argc);
126     for (i = 0; i < bprm->argc; ++i) {
127         size_t len = strlen(bprm->argv[i]) + 1;
128 
129         if (memcpy_to_target(destp, bprm->argv[i], len)) {
130             errno = EFAULT;
131             return -1;
132         }
133         if (put_user_ual(destp, argvp)) {
134             errno = EFAULT;
135             return -1;
136         }
137         argvp += sizeof(abi_ulong);
138         destp += len;
139     }
140     if (put_user_ual(0, argvp)) {
141         errno = EFAULT;
142         return -1;
143     }
144     /*
145      * Add env strings. Note that the envp[] vectors are added by
146      * loader_build_argptr().
147      */
148     envp = argvp + sizeof(abi_ulong);
149     ps_strs.ps_envstr = tswapl(envp);
150     ps_strs.ps_nenvstr = tswap32(bprm->envc);
151     for (i = 0; i < bprm->envc; ++i) {
152         size_t len = strlen(bprm->envp[i]) + 1;
153 
154         if (memcpy_to_target(destp, bprm->envp[i], len)) {
155             errno = EFAULT;
156             return -1;
157         }
158         if (put_user_ual(destp, envp)) {
159             errno = EFAULT;
160             return -1;
161         }
162         envp += sizeof(abi_ulong);
163         destp += len;
164     }
165     if (put_user_ual(0, envp)) {
166         errno = EFAULT;
167         return -1;
168     }
169     if (memcpy_to_target(stack_hi_addr - sizeof(ps_strs), &ps_strs,
170                 sizeof(ps_strs))) {
171         errno = EFAULT;
172         return -1;
173     }
174 
175     if (ret_addr) {
176         *ret_addr = p;
177     }
178 
179     return 0;
180  }
181 
182 #endif /* TARGET_OS_STACK_H */
183