xref: /qemu/bsd-user/freebsd/target_os_stack.h (revision 781fa471b08e5b3d02f2d38c254a711948a70366)
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 initial 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 dependent 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