njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 1 | |
| 2 | /*--------------------------------------------------------------------*/ |
| 3 | /*--- User-mode execve(), and other stuff shared between stage1 ---*/ |
| 4 | /*--- and stage2. m_ume.c ---*/ |
| 5 | /*--------------------------------------------------------------------*/ |
| 6 | |
| 7 | /* |
| 8 | This file is part of Valgrind, a dynamic binary instrumentation |
| 9 | framework. |
| 10 | |
Elliott Hughes | ed39800 | 2017-06-21 14:41:24 -0700 | [diff] [blame^] | 11 | Copyright (C) 2000-2017 Julian Seward |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 12 | jseward@acm.org |
| 13 | |
| 14 | This program is free software; you can redistribute it and/or |
| 15 | modify it under the terms of the GNU General Public License as |
| 16 | published by the Free Software Foundation; either version 2 of the |
| 17 | License, or (at your option) any later version. |
| 18 | |
| 19 | This program is distributed in the hope that it will be useful, but |
| 20 | WITHOUT ANY WARRANTY; without even the implied warranty of |
| 21 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 22 | General Public License for more details. |
| 23 | |
| 24 | You should have received a copy of the GNU General Public License |
| 25 | along with this program; if not, write to the Free Software |
| 26 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
| 27 | 02111-1307, USA. |
| 28 | |
| 29 | The GNU General Public License is contained in the file COPYING. |
| 30 | */ |
| 31 | |
| 32 | |
| 33 | #include "pub_core_basics.h" |
| 34 | #include "pub_core_vki.h" |
| 35 | |
| 36 | #include "pub_core_libcbase.h" |
| 37 | #include "pub_core_libcassert.h" // VG_(exit), vg_assert |
| 38 | #include "pub_core_libcfile.h" // VG_(close) et al |
| 39 | #include "pub_core_libcprint.h" // VG_(message) |
| 40 | #include "pub_core_mallocfree.h" // VG_(strdup) |
| 41 | #include "pub_core_syscall.h" // VG_(mk_SysRes_Error) |
| 42 | #include "pub_core_options.h" // VG_(clo_xml) |
| 43 | #include "pub_core_ume.h" // self |
| 44 | |
| 45 | #include "priv_ume.h" |
| 46 | |
| 47 | |
| 48 | typedef struct { |
florian | ff4a084 | 2015-04-08 19:01:15 +0000 | [diff] [blame] | 49 | Bool (*match_fn)(const void *hdr, SizeT len); |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 50 | Int (*load_fn)(Int fd, const HChar *name, ExeInfo *info); |
| 51 | } ExeHandler; |
| 52 | |
| 53 | static ExeHandler exe_handlers[] = { |
sewardj | 8eb8bab | 2015-07-21 14:44:28 +0000 | [diff] [blame] | 54 | # if defined(VGO_linux) || defined(VGO_solaris) |
njn | 8b68b64 | 2009-06-24 00:37:09 +0000 | [diff] [blame] | 55 | { VG_(match_ELF), VG_(load_ELF) }, |
sewardj | 6e9de46 | 2011-06-28 07:25:29 +0000 | [diff] [blame] | 56 | # elif defined(VGO_darwin) |
njn | 8b68b64 | 2009-06-24 00:37:09 +0000 | [diff] [blame] | 57 | { VG_(match_macho), VG_(load_macho) }, |
sewardj | 6e9de46 | 2011-06-28 07:25:29 +0000 | [diff] [blame] | 58 | # else |
| 59 | # error "unknown OS" |
| 60 | # endif |
njn | 8b68b64 | 2009-06-24 00:37:09 +0000 | [diff] [blame] | 61 | { VG_(match_script), VG_(load_script) }, |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 62 | }; |
| 63 | #define EXE_HANDLER_COUNT (sizeof(exe_handlers)/sizeof(exe_handlers[0])) |
| 64 | |
| 65 | |
| 66 | // Check the file looks executable. |
| 67 | SysRes |
| 68 | VG_(pre_exec_check)(const HChar* exe_name, Int* out_fd, Bool allow_setuid) |
| 69 | { |
| 70 | Int fd, ret, i; |
| 71 | SysRes res; |
| 72 | Char buf[4096]; |
florian | ff4a084 | 2015-04-08 19:01:15 +0000 | [diff] [blame] | 73 | SizeT bufsz = sizeof buf, fsz; |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 74 | Bool is_setuid = False; |
| 75 | |
| 76 | // Check it's readable |
| 77 | res = VG_(open)(exe_name, VKI_O_RDONLY, 0); |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 78 | if (sr_isError(res)) { |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 79 | return res; |
| 80 | } |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 81 | fd = sr_Res(res); |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 82 | |
| 83 | // Check we have execute permissions |
florian | 0af78ad | 2012-11-03 18:28:20 +0000 | [diff] [blame] | 84 | ret = VG_(check_executable)(&is_setuid, exe_name, allow_setuid); |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 85 | if (0 != ret) { |
| 86 | VG_(close)(fd); |
| 87 | if (is_setuid && !VG_(clo_xml)) { |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 88 | VG_(message)(Vg_UserMsg, "\n"); |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 89 | VG_(message)(Vg_UserMsg, |
tom | b7c2f9d | 2014-05-22 08:57:06 +0000 | [diff] [blame] | 90 | "Warning: Can't execute setuid/setgid/setcap executable: %s\n", |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 91 | exe_name); |
| 92 | VG_(message)(Vg_UserMsg, "Possible workaround: remove " |
sewardj | 738856f | 2009-07-15 14:48:32 +0000 | [diff] [blame] | 93 | "--trace-children=yes, if in effect\n"); |
| 94 | VG_(message)(Vg_UserMsg, "\n"); |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 95 | } |
| 96 | return VG_(mk_SysRes_Error)(ret); |
| 97 | } |
| 98 | |
| 99 | fsz = (SizeT)VG_(fsize)(fd); |
| 100 | if (fsz < bufsz) |
| 101 | bufsz = fsz; |
| 102 | |
| 103 | res = VG_(pread)(fd, buf, bufsz, 0); |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 104 | if (sr_isError(res) || sr_Res(res) != bufsz) { |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 105 | VG_(close)(fd); |
| 106 | return VG_(mk_SysRes_Error)(VKI_EACCES); |
| 107 | } |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 108 | bufsz = sr_Res(res); |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 109 | |
| 110 | // Look for a matching executable format |
| 111 | for (i = 0; i < EXE_HANDLER_COUNT; i++) { |
| 112 | if ((*exe_handlers[i].match_fn)(buf, bufsz)) { |
| 113 | res = VG_(mk_SysRes_Success)(i); |
| 114 | break; |
| 115 | } |
| 116 | } |
| 117 | if (i == EXE_HANDLER_COUNT) { |
| 118 | // Rejected by all executable format handlers. |
| 119 | res = VG_(mk_SysRes_Error)(VKI_ENOEXEC); |
| 120 | } |
| 121 | |
| 122 | // Write the 'out_fd' param if necessary, or close the file. |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 123 | if (!sr_isError(res) && out_fd) { |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 124 | *out_fd = fd; |
| 125 | } else { |
| 126 | VG_(close)(fd); |
| 127 | } |
| 128 | |
| 129 | return res; |
| 130 | } |
| 131 | |
| 132 | // returns: 0 = success, non-0 is failure |
| 133 | // |
| 134 | // We can execute only binaries (ELF, etc) or scripts that begin with "#!". |
florian | ff4a084 | 2015-04-08 19:01:15 +0000 | [diff] [blame] | 135 | // (Not, for example, scripts that don't begin with "#!"; see |
| 136 | // do_exec_shell_followup for how that's handled.) |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 137 | Int VG_(do_exec_inner)(const HChar* exe, ExeInfo* info) |
| 138 | { |
| 139 | SysRes res; |
| 140 | Int fd; |
| 141 | Int ret; |
| 142 | |
| 143 | res = VG_(pre_exec_check)(exe, &fd, False/*allow_setuid*/); |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 144 | if (sr_isError(res)) |
| 145 | return sr_Err(res); |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 146 | |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 147 | vg_assert2(sr_Res(res) >= 0 && sr_Res(res) < EXE_HANDLER_COUNT, |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 148 | "invalid VG_(pre_exec_check) result"); |
| 149 | |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 150 | ret = (*exe_handlers[sr_Res(res)].load_fn)(fd, exe, info); |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 151 | |
| 152 | VG_(close)(fd); |
| 153 | |
| 154 | return ret; |
| 155 | } |
| 156 | |
| 157 | |
florian | 0af78ad | 2012-11-03 18:28:20 +0000 | [diff] [blame] | 158 | static Bool is_hash_bang_file(const HChar* f) |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 159 | { |
| 160 | SysRes res = VG_(open)(f, VKI_O_RDONLY, 0); |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 161 | if (!sr_isError(res)) { |
florian | 0af78ad | 2012-11-03 18:28:20 +0000 | [diff] [blame] | 162 | HChar buf[3] = {0,0,0}; |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 163 | Int fd = sr_Res(res); |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 164 | Int n = VG_(read)(fd, buf, 2); |
| 165 | if (n == 2 && VG_STREQ("#!", buf)) |
| 166 | return True; |
| 167 | } |
| 168 | return False; |
| 169 | } |
| 170 | |
| 171 | // Look at the first 80 chars, and if any are greater than 127, it's binary. |
| 172 | // This is crude, but should be good enough. Note that it fails on a |
| 173 | // zero-length file, as we want. |
florian | 0af78ad | 2012-11-03 18:28:20 +0000 | [diff] [blame] | 174 | static Bool is_binary_file(const HChar* f) |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 175 | { |
| 176 | SysRes res = VG_(open)(f, VKI_O_RDONLY, 0); |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 177 | if (!sr_isError(res)) { |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 178 | UChar buf[80]; |
njn | cda2f0f | 2009-05-18 02:12:08 +0000 | [diff] [blame] | 179 | Int fd = sr_Res(res); |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 180 | Int n = VG_(read)(fd, buf, 80); |
| 181 | Int i; |
| 182 | for (i = 0; i < n; i++) { |
| 183 | if (buf[i] > 127) |
| 184 | return True; // binary char found |
| 185 | } |
| 186 | return False; |
| 187 | } else { |
| 188 | // Something went wrong. This will only happen if we earlier |
| 189 | // succeeded in opening the file but fail here (eg. the file was |
| 190 | // deleted between then and now). |
njn | b1cc5d6 | 2010-07-06 04:05:23 +0000 | [diff] [blame] | 191 | VG_(fmsg)("%s: unknown error\n", f); |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 192 | VG_(exit)(126); // 126 == NOEXEC |
| 193 | } |
| 194 | } |
| 195 | |
| 196 | // If the do_exec fails we try to emulate what the shell does (I used |
| 197 | // bash as a guide). It's worth noting that the shell can execute some |
| 198 | // things that VG_(do_exec)() (which subsitutes for the kernel's exec()) |
| 199 | // will refuse to (eg. scripts lacking a "#!" prefix). |
florian | 0af78ad | 2012-11-03 18:28:20 +0000 | [diff] [blame] | 200 | static Int do_exec_shell_followup(Int ret, const HChar* exe_name, ExeInfo* info) |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 201 | { |
sewardj | 26ed419 | 2014-11-04 17:44:21 +0000 | [diff] [blame] | 202 | # if defined(VGPV_arm_linux_android) \ |
| 203 | || defined(VGPV_x86_linux_android) \ |
| 204 | || defined(VGPV_mips32_linux_android) \ |
| 205 | || defined(VGPV_arm64_linux_android) |
florian | 0af78ad | 2012-11-03 18:28:20 +0000 | [diff] [blame] | 206 | const HChar* default_interp_name = "/system/bin/sh"; |
sewardj | 5138111 | 2011-07-12 06:19:05 +0000 | [diff] [blame] | 207 | # else |
florian | 0af78ad | 2012-11-03 18:28:20 +0000 | [diff] [blame] | 208 | const HChar* default_interp_name = "/bin/sh"; |
sewardj | 5138111 | 2011-07-12 06:19:05 +0000 | [diff] [blame] | 209 | # endif |
| 210 | |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 211 | SysRes res; |
| 212 | struct vg_stat st; |
| 213 | |
| 214 | if (VKI_ENOEXEC == ret) { |
| 215 | // It was an executable file, but in an unacceptable format. Probably |
| 216 | // is a shell script lacking the "#!" prefix; try to execute it so. |
| 217 | |
| 218 | // Is it a binary file? |
| 219 | if (is_binary_file(exe_name)) { |
njn | b1cc5d6 | 2010-07-06 04:05:23 +0000 | [diff] [blame] | 220 | VG_(fmsg)("%s: cannot execute binary file\n", exe_name); |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 221 | VG_(exit)(126); // 126 == NOEXEC |
| 222 | } |
| 223 | |
| 224 | // Looks like a script. Run it with /bin/sh. This includes |
| 225 | // zero-length files. |
Elliott Hughes | a0664b9 | 2017-04-18 17:46:52 -0700 | [diff] [blame] | 226 | VG_(free)(info->interp_name); |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 227 | info->interp_name = VG_(strdup)("ume.desf.1", default_interp_name); |
Elliott Hughes | a0664b9 | 2017-04-18 17:46:52 -0700 | [diff] [blame] | 228 | VG_(free)(info->interp_args); info->interp_args = NULL; |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 229 | if (info->argv && info->argv[0] != NULL) |
florian | 3e79863 | 2012-11-24 19:41:54 +0000 | [diff] [blame] | 230 | info->argv[0] = exe_name; |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 231 | |
| 232 | ret = VG_(do_exec_inner)(info->interp_name, info); |
| 233 | |
| 234 | if (0 != ret) { |
| 235 | // Something went wrong with executing the default interpreter |
njn | b1cc5d6 | 2010-07-06 04:05:23 +0000 | [diff] [blame] | 236 | VG_(fmsg)("%s: bad interpreter (%s): %s\n", |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 237 | exe_name, info->interp_name, VG_(strerror)(ret)); |
| 238 | VG_(exit)(126); // 126 == NOEXEC |
| 239 | } |
| 240 | |
| 241 | } else if (0 != ret) { |
| 242 | // Something else went wrong. Try to make the error more specific, |
| 243 | // and then print a message and abort. |
florian | ff4a084 | 2015-04-08 19:01:15 +0000 | [diff] [blame] | 244 | Int exit_code = 126; // 126 == NOEXEC (bash) |
| 245 | |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 246 | res = VG_(stat)(exe_name, &st); |
florian | ff4a084 | 2015-04-08 19:01:15 +0000 | [diff] [blame] | 247 | |
| 248 | // Does the file exist ? |
| 249 | if (sr_isError(res) && sr_Err(res) == VKI_ENOENT) { |
| 250 | VG_(fmsg)("%s: %s\n", exe_name, VG_(strerror)(ret)); |
| 251 | exit_code = 127; // 127 == NOTFOUND (bash) |
| 252 | |
| 253 | // Was it a directory? |
| 254 | } else if (!sr_isError(res) && VKI_S_ISDIR(st.mode)) { |
njn | b1cc5d6 | 2010-07-06 04:05:23 +0000 | [diff] [blame] | 255 | VG_(fmsg)("%s: is a directory\n", exe_name); |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 256 | |
| 257 | // Was it not executable? |
| 258 | } else if (0 != VG_(check_executable)(NULL, exe_name, |
| 259 | False/*allow_setuid*/)) { |
njn | b1cc5d6 | 2010-07-06 04:05:23 +0000 | [diff] [blame] | 260 | VG_(fmsg)("%s: %s\n", exe_name, VG_(strerror)(ret)); |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 261 | |
| 262 | // Did it start with "#!"? If so, it must have been a bad interpreter. |
| 263 | } else if (is_hash_bang_file(exe_name)) { |
njn | b1cc5d6 | 2010-07-06 04:05:23 +0000 | [diff] [blame] | 264 | VG_(fmsg)("%s: bad interpreter: %s\n", exe_name, VG_(strerror)(ret)); |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 265 | |
| 266 | // Otherwise it was something else. |
| 267 | } else { |
njn | b1cc5d6 | 2010-07-06 04:05:23 +0000 | [diff] [blame] | 268 | VG_(fmsg)("%s: %s\n", exe_name, VG_(strerror)(ret)); |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 269 | } |
florian | ff4a084 | 2015-04-08 19:01:15 +0000 | [diff] [blame] | 270 | VG_(exit)(exit_code); |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 271 | } |
| 272 | return ret; |
| 273 | } |
| 274 | |
| 275 | |
| 276 | // This emulates the kernel's exec(). If it fails, it then emulates the |
| 277 | // shell's handling of the situation. |
florian | ff4a084 | 2015-04-08 19:01:15 +0000 | [diff] [blame] | 278 | // See pub_core_ume.h for an indication of which entries of 'info' are |
| 279 | // inputs, which are outputs, and which are both. |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 280 | /* returns: 0 = success, non-0 is failure */ |
| 281 | Int VG_(do_exec)(const HChar* exe_name, ExeInfo* info) |
| 282 | { |
| 283 | Int ret; |
Elliott Hughes | a0664b9 | 2017-04-18 17:46:52 -0700 | [diff] [blame] | 284 | |
| 285 | VG_(free)(info->interp_name); info->interp_name = NULL; |
| 286 | VG_(free)(info->interp_args); info->interp_args = NULL; |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 287 | |
| 288 | ret = VG_(do_exec_inner)(exe_name, info); |
| 289 | |
| 290 | if (0 != ret) { |
florian | 0af78ad | 2012-11-03 18:28:20 +0000 | [diff] [blame] | 291 | ret = do_exec_shell_followup(ret, exe_name, info); |
njn | 91772d1 | 2009-01-21 02:26:56 +0000 | [diff] [blame] | 292 | } |
| 293 | return ret; |
| 294 | } |
| 295 | |
| 296 | /*--------------------------------------------------------------------*/ |
| 297 | /*--- end ---*/ |
| 298 | /*--------------------------------------------------------------------*/ |