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