mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-03 18:13:01 +00:00
the DARWIN branch easier later. - Remove the disabled vgtest_ume test, it's very unlikely it'll ever work again. - Move VG_(find_auxv) to initimg-linux.c, the only place it's used, and make it static. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@9004
287 lines
8.9 KiB
C
287 lines
8.9 KiB
C
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- User-mode execve(), and other stuff shared between stage1 ---*/
|
|
/*--- and stage2. m_ume.c ---*/
|
|
/*--------------------------------------------------------------------*/
|
|
|
|
/*
|
|
This file is part of Valgrind, a dynamic binary instrumentation
|
|
framework.
|
|
|
|
Copyright (C) 2000-2008 Julian Seward
|
|
jseward@acm.org
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the
|
|
License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
02111-1307, USA.
|
|
|
|
The GNU General Public License is contained in the file COPYING.
|
|
*/
|
|
|
|
|
|
#include "pub_core_basics.h"
|
|
#include "pub_core_vki.h"
|
|
|
|
#include "pub_core_libcbase.h"
|
|
#include "pub_core_libcassert.h" // VG_(exit), vg_assert
|
|
#include "pub_core_libcfile.h" // VG_(close) et al
|
|
#include "pub_core_libcprint.h" // VG_(message)
|
|
#include "pub_core_mallocfree.h" // VG_(strdup)
|
|
#include "pub_core_syscall.h" // VG_(mk_SysRes_Error)
|
|
#include "pub_core_options.h" // VG_(clo_xml)
|
|
#include "pub_core_ume.h" // self
|
|
|
|
#include "priv_ume.h"
|
|
|
|
|
|
typedef struct {
|
|
const HChar *name;
|
|
Bool (*match_fn)(Char *hdr, Int len);
|
|
Int (*load_fn)(Int fd, const HChar *name, ExeInfo *info);
|
|
} ExeHandler;
|
|
|
|
static ExeHandler exe_handlers[] = {
|
|
#if defined(HAVE_ELF)
|
|
{ "ELF", VG_(match_ELF), VG_(load_ELF) },
|
|
#endif
|
|
#if defined(HAVE_SCRIPT)
|
|
{ "script", VG_(match_script), VG_(load_script) },
|
|
#endif
|
|
};
|
|
#define EXE_HANDLER_COUNT (sizeof(exe_handlers)/sizeof(exe_handlers[0]))
|
|
|
|
|
|
// Check the file looks executable.
|
|
SysRes
|
|
VG_(pre_exec_check)(const HChar* exe_name, Int* out_fd, Bool allow_setuid)
|
|
{
|
|
Int fd, ret, i;
|
|
SysRes res;
|
|
Char buf[4096];
|
|
SizeT bufsz = 4096, fsz;
|
|
Bool is_setuid = False;
|
|
|
|
// Check it's readable
|
|
res = VG_(open)(exe_name, VKI_O_RDONLY, 0);
|
|
if (res.isError) {
|
|
return res;
|
|
}
|
|
fd = res.res;
|
|
|
|
// Check we have execute permissions
|
|
ret = VG_(check_executable)(&is_setuid, (HChar*)exe_name, allow_setuid);
|
|
if (0 != ret) {
|
|
VG_(close)(fd);
|
|
if (is_setuid && !VG_(clo_xml)) {
|
|
VG_(message)(Vg_UserMsg, "");
|
|
VG_(message)(Vg_UserMsg,
|
|
"Warning: Can't execute setuid/setgid executable: %s",
|
|
exe_name);
|
|
VG_(message)(Vg_UserMsg, "Possible workaround: remove "
|
|
"--trace-children=yes, if in effect");
|
|
VG_(message)(Vg_UserMsg, "");
|
|
}
|
|
return VG_(mk_SysRes_Error)(ret);
|
|
}
|
|
|
|
fsz = (SizeT)VG_(fsize)(fd);
|
|
if (fsz < bufsz)
|
|
bufsz = fsz;
|
|
|
|
res = VG_(pread)(fd, buf, bufsz, 0);
|
|
if (res.isError || res.res != bufsz) {
|
|
VG_(close)(fd);
|
|
return VG_(mk_SysRes_Error)(VKI_EACCES);
|
|
}
|
|
bufsz = res.res;
|
|
|
|
// Look for a matching executable format
|
|
for (i = 0; i < EXE_HANDLER_COUNT; i++) {
|
|
if ((*exe_handlers[i].match_fn)(buf, bufsz)) {
|
|
res = VG_(mk_SysRes_Success)(i);
|
|
break;
|
|
}
|
|
}
|
|
if (i == EXE_HANDLER_COUNT) {
|
|
// Rejected by all executable format handlers.
|
|
res = VG_(mk_SysRes_Error)(VKI_ENOEXEC);
|
|
}
|
|
|
|
// Write the 'out_fd' param if necessary, or close the file.
|
|
if (!res.isError && out_fd) {
|
|
*out_fd = fd;
|
|
} else {
|
|
VG_(close)(fd);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
// returns: 0 = success, non-0 is failure
|
|
//
|
|
// We can execute only binaries (ELF, etc) or scripts that begin with "#!".
|
|
// (Not, for example, scripts that don't begin with "#!"; see the
|
|
// VG_(do_exec)() invocation from m_main.c for how that's handled.)
|
|
Int VG_(do_exec_inner)(const HChar* exe, ExeInfo* info)
|
|
{
|
|
SysRes res;
|
|
Int fd;
|
|
Int ret;
|
|
|
|
res = VG_(pre_exec_check)(exe, &fd, False/*allow_setuid*/);
|
|
if (res.isError)
|
|
return res.err;
|
|
|
|
vg_assert2(res.res >= 0 && res.res < EXE_HANDLER_COUNT,
|
|
"invalid VG_(pre_exec_check) result");
|
|
|
|
ret = (*exe_handlers[res.res].load_fn)(fd, exe, info);
|
|
|
|
VG_(close)(fd);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static Bool is_hash_bang_file(Char* f)
|
|
{
|
|
SysRes res = VG_(open)(f, VKI_O_RDONLY, 0);
|
|
if (!res.isError) {
|
|
Char buf[3] = {0,0,0};
|
|
Int fd = res.res;
|
|
Int n = VG_(read)(fd, buf, 2);
|
|
if (n == 2 && VG_STREQ("#!", buf))
|
|
return True;
|
|
}
|
|
return False;
|
|
}
|
|
|
|
// Look at the first 80 chars, and if any are greater than 127, it's binary.
|
|
// This is crude, but should be good enough. Note that it fails on a
|
|
// zero-length file, as we want.
|
|
static Bool is_binary_file(Char* f)
|
|
{
|
|
SysRes res = VG_(open)(f, VKI_O_RDONLY, 0);
|
|
if (!res.isError) {
|
|
UChar buf[80];
|
|
Int fd = res.res;
|
|
Int n = VG_(read)(fd, buf, 80);
|
|
Int i;
|
|
for (i = 0; i < n; i++) {
|
|
if (buf[i] > 127)
|
|
return True; // binary char found
|
|
}
|
|
return False;
|
|
} else {
|
|
// Something went wrong. This will only happen if we earlier
|
|
// succeeded in opening the file but fail here (eg. the file was
|
|
// deleted between then and now).
|
|
VG_(printf)("valgrind: %s: unknown error\n", f);
|
|
VG_(exit)(126); // 126 == NOEXEC
|
|
}
|
|
}
|
|
|
|
// If the do_exec fails we try to emulate what the shell does (I used
|
|
// bash as a guide). It's worth noting that the shell can execute some
|
|
// things that VG_(do_exec)() (which subsitutes for the kernel's exec())
|
|
// will refuse to (eg. scripts lacking a "#!" prefix).
|
|
static Int do_exec_shell_followup(Int ret, HChar* exe_name, ExeInfo* info)
|
|
{
|
|
Char* default_interp_name = "/bin/sh";
|
|
SysRes res;
|
|
struct vg_stat st;
|
|
|
|
if (VKI_ENOEXEC == ret) {
|
|
// It was an executable file, but in an unacceptable format. Probably
|
|
// is a shell script lacking the "#!" prefix; try to execute it so.
|
|
|
|
// Is it a binary file?
|
|
if (is_binary_file(exe_name)) {
|
|
VG_(printf)("valgrind: %s: cannot execute binary file\n", exe_name);
|
|
VG_(exit)(126); // 126 == NOEXEC
|
|
}
|
|
|
|
// Looks like a script. Run it with /bin/sh. This includes
|
|
// zero-length files.
|
|
|
|
info->interp_name = VG_(strdup)("ume.desf.1", default_interp_name);
|
|
info->interp_args = NULL;
|
|
if (info->argv && info->argv[0] != NULL)
|
|
info->argv[0] = (char *)exe_name;
|
|
|
|
ret = VG_(do_exec_inner)(info->interp_name, info);
|
|
|
|
if (0 != ret) {
|
|
// Something went wrong with executing the default interpreter
|
|
VG_(printf)("valgrind: %s: bad interpreter (%s): %s\n",
|
|
exe_name, info->interp_name, VG_(strerror)(ret));
|
|
VG_(exit)(126); // 126 == NOEXEC
|
|
}
|
|
|
|
} else if (0 != ret) {
|
|
// Something else went wrong. Try to make the error more specific,
|
|
// and then print a message and abort.
|
|
|
|
// Was it a directory?
|
|
res = VG_(stat)(exe_name, &st);
|
|
if (!res.isError && VKI_S_ISDIR(st.st_mode)) {
|
|
VG_(printf)("valgrind: %s: is a directory\n", exe_name);
|
|
|
|
// Was it not executable?
|
|
} else if (0 != VG_(check_executable)(NULL, exe_name,
|
|
False/*allow_setuid*/)) {
|
|
VG_(printf)("valgrind: %s: %s\n", exe_name, VG_(strerror)(ret));
|
|
|
|
// Did it start with "#!"? If so, it must have been a bad interpreter.
|
|
} else if (is_hash_bang_file(exe_name)) {
|
|
VG_(printf)("valgrind: %s: bad interpreter: %s\n",
|
|
exe_name, VG_(strerror)(ret));
|
|
|
|
// Otherwise it was something else.
|
|
} else {
|
|
VG_(printf)("valgrind: %s: %s\n", exe_name, VG_(strerror)(ret));
|
|
}
|
|
// 126 means NOEXEC; I think this is Posix, and that in some cases we
|
|
// should be returning 127, meaning NOTFOUND. Oh well.
|
|
VG_(exit)(126);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
// This emulates the kernel's exec(). If it fails, it then emulates the
|
|
// shell's handling of the situation.
|
|
// See ume.h for an indication of which entries of 'info' are inputs, which
|
|
// are outputs, and which are both.
|
|
/* returns: 0 = success, non-0 is failure */
|
|
Int VG_(do_exec)(const HChar* exe_name, ExeInfo* info)
|
|
{
|
|
Int ret;
|
|
|
|
info->interp_name = NULL;
|
|
info->interp_args = NULL;
|
|
|
|
ret = VG_(do_exec_inner)(exe_name, info);
|
|
|
|
if (0 != ret) {
|
|
Char* exe_name_casted = (Char*)exe_name;
|
|
ret = do_exec_shell_followup(ret, exe_name_casted, info);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- end ---*/
|
|
/*--------------------------------------------------------------------*/
|