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