This is all quite messy.
It affects open() openat() and openat2() (the last of which is Linux only).
On Linux we also need to check for /proc/self/exe and /proc/PID/exe.
On Linux there are also a couple of RESOLVE flags for openat2() that
mean _don't_ check /proc magic links.
In the general case we need to have some reference to check whether
the filename matches the guest filename. So I've added that as
VG_(resolved_exename) (which I was already using on FreeBSD).
The pathname also needs to be canonicalised. It may be a
relative path, symlink or use RESOLVE_IN_ROOT. That uses
VG_(realpath) (again which was already present for FreBSD).
On illumos the man page says that opening running binaries for
writing failes with errno set to ETXTBSY but that's not what
the open functions do - they just open the file. So I've done nothing
for illumos or Solaris. Maybe I'll open an illumos ticket.
I haven't tried on Darwin.
The Linux open functions with /proc/self/exe and /proc/PID/exe
were just calling dup on the fd that we hold for the client exe.
That means that we were ignoring any other flags. That has now changed.
If the open doesn't fail because the WRONLY/RDWR flags are set then
the syscall gets called from the PRE wrapper using VG_(resolved_exename)
instewad of the /proc pathname.
I haven't tried to handle all of the Linux openat2 RESOLVE*
flags. RESOLVE_NO_MAGICLINKS is handled and I see the LTS test
openat202 now passing, so this should also fix Bug 506910.
I'm not sure that VG_(realpath) handles all forms of weird path
resolution on Linux (on FreeBSD it uses a syscall so that should
work OK).
/none/tests/linux/mremap4
/none/tests/linux/mremap5
/none/tests/linux/mremap6
+/none/tests/linux/open_client
/none/tests/linux/pthread-stack
/none/tests/linux/stack-overflow
/none/tests/freebsd/swapcontext
/none/tests/freebsd/fexecve
/none/tests/freebsd/hello_world
+/none/tests/freebsd/open_client
/none/tests/freebsd/proc_pid_file
/none/tests/freebsd/sanity_level_thread
/none/tests/freebsd/usrstack
AMD64_GET_TLSBASE
505228 Wrap linux specific mseal syscall
502968 Wrap linux specific syscalls 457 (listmount) and 458 (statmount)
+505673 Valgrind crashes with an internal error and SIGBUS when
+ the guest tries to open its own file with O_WRONLY|O_CREAT|O_TRUNC
506076 unimplemented fcntl command: 1028 (F_CREATED_QUERY)
506499 Unhandled syscall 592 (exterrctl - FreeBSD
506795 Better report which clone flags are problematic
return (const struct auxv *)sp;
}
+/*
+ * @todo PJF Make this multi-platform
+ */
static Bool try_get_interp(const HChar* args_exe, HChar* interp_out)
{
HChar hdr[4096];
return (struct auxv *)sp;
}
+/*
+ * @todo PJF Make this multi-platform
+ */
+static Bool try_get_interp(const HChar* args_exe, HChar* interp_out)
+{
+ HChar hdr[4096];
+ Int len = sizeof hdr;
+ SysRes res;
+ Int fd;
+ HChar* end;
+ HChar* cp;
+ HChar* interp;
+
+ res = VG_(open)(args_exe, VKI_O_RDONLY, 0);
+ if (sr_isError(res)) {
+ return False;
+ } else {
+ fd = sr_Res(res);
+ }
+
+ res = VG_(pread)(fd, hdr, len, 0);
+
+ if (sr_isError(res)) {
+ VG_(close)(fd);
+ return False;
+ } else {
+ len = sr_Res(res);
+ }
+
+ if (0 != VG_(memcmp)(hdr, "#!", 2)) {
+ VG_(close)(fd);
+ return False;
+ }
+
+ end = hdr + len;
+ interp = hdr + 2;
+ while (interp < end && (*interp == ' ' || *interp == '\t'))
+ interp++;
+
+ for (cp = interp; cp < end && !VG_(isspace)(*cp); cp++)
+ ;
+
+ *cp = '\0';
+
+ VG_(sprintf)(interp_out, "%s", interp);
+
+ VG_(close)(fd);
+ return True;
+}
+
static
Addr setup_client_stack( void* init_sp,
- HChar** orig_envp,
+ HChar** orig_envp,
const ExeInfo* info,
UInt** client_auxv,
Addr clstack_end,
vg_assert((strtab-stringbase) == stringsize);
+ if (VG_(resolved_exename) == NULL) {
+ const HChar *exe_name = VG_(find_executable)(VG_(args_the_exename));
+ HChar interp_name[VKI_PATH_MAX];
+ if (try_get_interp(exe_name, interp_name)) {
+ exe_name = interp_name;
+ }
+ HChar resolved_name[VKI_PATH_MAX];
+ VG_(realpath)(exe_name, resolved_name);
+ VG_(resolved_exename) = VG_(strdup)("initimg-linux.sre.1", resolved_name);
+ }
+
/* client_SP is pointing at client's argc/argv */
if (0) VG_(printf)("startup SP = %#lx\n", client_SP);
# endif
}
-#if defined(VGO_freebsd)
/* extend this to other OSes as and when needed */
SysRes VG_(lstat) ( const HChar* file_name, struct vg_stat* vgbuf )
{
SysRes res;
VG_(memset)(vgbuf, 0, sizeof(*vgbuf));
-#if (__FreeBSD_version < 1200031)
+#if !defined(VGO_freebsd) || (__FreeBSD_version < 1200031)
+#if defined(VGO_freebsd)
struct vki_freebsd11_stat buf;
+#else
+ struct vki_stat buf;
+#endif
res = VG_(do_syscall2)(__NR_lstat, (UWord)file_name, (UWord)&buf);
#else
struct vki_stat buf;
}
return res;
}
-#endif
#undef TRANSLATE_TO_vg_stat
#undef TRANSLATE_statx_TO_vg_stat
return buf;
}
-#if defined(VGO_freebsd)
/*
* I did look at nicking this from FreeBSD, it's fairly easy to port
* but I was put off by the copyright and 3-clause licence
{
vg_assert(path);
vg_assert(resolved);
-#if (__FreeBSD_version >= 1300080)
+#if defined(VGO_freebsd) && (__FreeBSD_version >= 1300080)
return !sr_isError(VG_(do_syscall5)(__NR___realpathat, VKI_AT_FDCWD, (RegWord)path, (RegWord)resolved, VKI_PATH_MAX, 0));
#else
// poor man's realpath
if (resolved_name[0] == '.' && resolved_name[1] == '/') {
resolved_name += 2;
}
- VG_(snprintf)(resolved, VKI_PATH_MAX, "%s/%s", VG_(get_startup_wd)(), resolved_name);
+ HChar wd[VKI_PATH_MAX];
+#if defined(VGO_linux) || defined(VGO_solaris)
+ res = VG_(do_syscall2)(__NR_getcwd, (UWord)wd, VKI_PATH_MAX);
+#elif defined(VGO_freebsd)
+ res = VG_(do_syscall2)(__NR___getcwd, (UWord)wd, VKI_PATH_MAX);
+#endif
+ VG_(snprintf)(resolved, VKI_PATH_MAX, "%s/%s", wd, resolved_name);
} else {
VG_(snprintf)(resolved, VKI_PATH_MAX, "%s", resolved_name);
}
return True;
#endif
}
-#endif
/*--------------------------------------------------------------------*/
// int openat(int fd, const char *path, int flags, ...);
PRE(sys_openat)
{
+ // check that we are not trying to open the client exe for writing
+ if ((ARG3 & VKI_O_WRONLY) ||
+ (ARG3 & VKI_O_RDWR)) {
+ vg_assert(VG_(resolved_exename) && VG_(resolved_exename)[0] == '/');
+ Int fd = ARG1;
+ const HChar* path = (const HChar*)ARG2;
+ if (ML_(safe_to_deref)(path, 1)) { // we need something like a "ML_(safe_to_deref_path)" that does a binary search for the addressable length, and maybe nul
+ if (fd == VKI_AT_FDCWD) {
+ HChar tmp[VKI_PATH_MAX];
+ if (VG_(realpath)(path, tmp)) {
+ if (!VG_(strcmp)(tmp, VG_(resolved_exename))) {
+ SET_STATUS_Failure( VKI_ETXTBSY );
+ }
+ }
+ } else {
+ const HChar* dirname;
+ if (VG_(resolve_filename)(fd, &dirname) == False) {
+ goto no_client_write; // let the OS do the error handling
+ }
+ HChar tmp1[VKI_PATH_MAX];
+ VG_(snprintf)(tmp1, VKI_PATH_MAX, "%s/%s", dirname, path);
+ tmp1[VKI_PATH_MAX - 1] = '\0';
+ //VG_(free)((void*)dirname);
+ HChar tmp2[VKI_PATH_MAX];
+ if (VG_(realpath)(tmp1, tmp2)) {
+ if (!VG_(strcmp)(tmp2, VG_(resolved_exename))) {
+ SET_STATUS_Failure( VKI_ETXTBSY );
+ }
+ }
+ }
+ }
+ }
+no_client_write:
if (ARG3 & VKI_O_CREAT) {
// 4-arg version
PRINT("sys_openat ( %" FMT_REGWORD "u, %#" FMT_REGWORD "x(%s), %" FMT_REGWORD "u, %" FMT_REGWORD "u )",ARG1,ARG2,(char*)ARG2,ARG3,ARG4);
}
#endif // defined(VGO_linux)
+// int open(const char *path, int flags, mode_t mode);
PRE(sys_open)
{
+#if defined(VGO_linux)
+ HChar name[30]; // large enough
+ Bool proc_self_exe = False;
+
+ /* Check for /proc/self/exe or /proc/<pid>/exe case
+ * first so that we can then use the later checks. */
+ VG_(sprintf)(name, "/proc/%d/exe", VG_(getpid)());
+ if (ML_(safe_to_deref)( (void*)(Addr)ARG1, 1 )
+ && (VG_(strcmp)((HChar *)(Addr)ARG1, name) == 0
+ || VG_(strcmp)((HChar *)(Addr)ARG1, "/proc/self/exe") == 0)) {
+ proc_self_exe = True;
+ }
+#endif
+
if (ARG2 & VKI_O_CREAT) {
// 3-arg version
PRINT("sys_open ( %#" FMT_REGWORD "x(%s), %ld, %ld )",ARG1,
}
PRE_MEM_RASCIIZ( "open(filename)", ARG1 );
+ // check that we are not trying to open the client exe for writing
+ if ((ARG2 & VKI_O_WRONLY) ||
+ (ARG2 & VKI_O_RDWR)) {
+#if defined(VGO_linux)
+ if (proc_self_exe) {
+
+ SET_STATUS_Failure( VKI_ETXTBSY );
+ return;
+ } else {
+#endif
+ vg_assert(VG_(resolved_exename));
+ const HChar* path = (const HChar*)ARG1;
+ if (ML_(safe_to_deref)(path, 1)) {
+ // we need something like a "ML_(safe_to_deref_path)" that does a binary search for the addressable length, and maybe nul
+ HChar tmp[VKI_PATH_MAX];
+ if (VG_(realpath)(path, tmp)) {
+ if (!VG_(strcmp)(tmp, VG_(resolved_exename))) {
+ SET_STATUS_Failure( VKI_ETXTBSY );
+ return;
+ }
+ }
+ }
+#if defined(VGO_linux)
+ }
+#endif
+ }
+
#if defined(VGO_linux)
/* Handle the case where the open is of /proc/self/cmdline or
/proc/<pid>/cmdline, and just give it a copy of the fd for the
fake file we cooked up at startup (in m_main). Also, seek the
cloned fd back to the start. */
{
- HChar name[30]; // large enough
HChar* arg1s = (HChar*) (Addr)ARG1;
SysRes sres;
if (ML_(handle_auxv_open)(status, (const HChar *)(Addr)ARG1, ARG2)
|| ML_(handle_self_exe_open)(status, (const HChar *)(Addr)ARG1, ARG2))
return;
+
+ if (proc_self_exe) {
+ // do the syscall with VG_(resolved_exename)
+ SET_STATUS_from_SysRes(VG_(do_syscall3)(SYSNO, (Word)VG_(resolved_exename), ARG2, ARG3));
+ }
#endif // defined(VGO_linux)
/* Otherwise handle normally */
{
HChar name[30]; // large enough
SysRes sres;
+ Bool proc_self_exe = False;
+
+ /* Check for /proc/self/exe or /proc/<pid>/exe case
+ * first so that we can then use the later checks. */
+ VG_(sprintf)(name, "/proc/%d/exe", VG_(getpid)());
+ if (ML_(safe_to_deref)( (void*)(Addr)ARG2, 1 )
+ && (VG_(strcmp)((HChar *)(Addr)ARG2, name) == 0
+ || VG_(strcmp)((HChar *)(Addr)ARG2, "/proc/self/exe") == 0)) {
+ proc_self_exe = True;
+ }
if (ARG3 & VKI_O_CREAT) {
// 4-arg version
PRINT("sys_openat ( %ld, %#" FMT_REGWORD "x(%s), %ld, %ld )",
SARG1, ARG2, (HChar*)(Addr)ARG2, SARG3, SARG4);
PRE_REG_READ4(long, "openat",
- int, dfd, const char *, filename, int, flags, int, mode);
+ int, dirfd, const char *, pathname, int, flags, int, mode);
} else {
// 3-arg version
PRINT("sys_openat ( %ld, %#" FMT_REGWORD "x(%s), %ld )",
SARG1, ARG2, (HChar*)(Addr)ARG2, SARG3);
PRE_REG_READ3(long, "openat",
- int, dfd, const char *, filename, int, flags);
+ int, dirfd, const char *, pathname, int, flags);
}
- PRE_MEM_RASCIIZ( "openat(filename)", ARG2 );
+ PRE_MEM_RASCIIZ( "openat(pathname)", ARG2 );
- /* For absolute filenames, dfd is ignored. If dfd is AT_FDCWD,
- filename is relative to cwd. When comparing dfd against AT_FDCWD,
+ // check that we are not trying to open the client exe for writing
+ if ((ARG3 & VKI_O_WRONLY) ||
+ (ARG3 & VKI_O_RDWR)) {
+ if (proc_self_exe) {
+ SET_STATUS_Failure( VKI_ETXTBSY );
+ return;
+ } else {
+ vg_assert(VG_(resolved_exename));
+ if (!VG_(strncmp)(VG_(resolved_exename), "#!", 2)) {
+ goto no_client_write;
+ }
+ Int fd = ARG1;
+ const HChar* path = (const HChar*)ARG2;
+ if (ML_(safe_to_deref)(path, 1)) {
+ // we need something like a "ML_(safe_to_deref_path)" that does a binary search for the addressable length, and maybe nul
+ if (path[0] == '/') {
+ // ignore fd
+ HChar tmp[VKI_PATH_MAX];
+ if (VG_(realpath)(path, tmp)) {
+ if (!VG_(strcmp)(tmp, VG_(resolved_exename))) {
+ SET_STATUS_Failure( VKI_ETXTBSY );
+ return;
+ }
+ }
+ } else {
+ if (fd == VKI_AT_FDCWD) {
+ HChar tmp[VKI_PATH_MAX];
+ if (VG_(realpath)(path, tmp)) {
+ if (!VG_(strcmp)(tmp, VG_(resolved_exename))) {
+ SET_STATUS_Failure( VKI_ETXTBSY );
+ }
+ }
+ } else {
+ const HChar* dirname;
+ if (VG_(resolve_filename)(fd, &dirname) == False) {
+ goto no_client_write; // let the OS do the error handling
+ }
+ HChar tmp1[VKI_PATH_MAX];
+ VG_(snprintf)(tmp1, VKI_PATH_MAX, "%s/%s", dirname, path);
+ tmp1[VKI_PATH_MAX - 1] = '\0';
+ HChar tmp2[VKI_PATH_MAX];
+ if (VG_(realpath)(tmp1, tmp2)) {
+ if (!VG_(strcmp)(tmp2, VG_(resolved_exename))) {
+ SET_STATUS_Failure( VKI_ETXTBSY );
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+no_client_write:
+
+ /* For absolute filenames, dirfd is ignored. If dirfd is AT_FDCWD,
+ filename is relative to cwd. When comparing dirfd against AT_FDCWD,
be sure only to compare the bottom 32 bits. */
if (ML_(safe_to_deref)( (void*)(Addr)ARG2, 1 )
&& *(Char *)(Addr)ARG2 != '/'
return;
}
- /* And for /proc/self/exe or /proc/<pid>/exe case. */
+ if (proc_self_exe) {
- VG_(sprintf)(name, "/proc/%d/exe", VG_(getpid)());
- if (ML_(safe_to_deref)( (void*)(Addr)ARG2, 1 )
- && (VG_(strcmp)((HChar *)(Addr)ARG2, name) == 0
- || VG_(strcmp)((HChar *)(Addr)ARG2, "/proc/self/exe") == 0)) {
- sres = VG_(dup)( VG_(cl_exec_fd) );
- SET_STATUS_from_SysRes( sres );
- if (!sr_isError(sres)) {
- OffT off = VG_(lseek)( sr_Res(sres), 0, VKI_SEEK_SET );
- if (off < 0)
- SET_STATUS_Failure( VKI_EMFILE );
- }
- return;
+ // do the syscall with VG_(resolved_exename)
+ SET_STATUS_from_SysRes(VG_(do_syscall4)(SYSNO, ARG1, (Word)VG_(resolved_exename), ARG3, ARG4));
}
/* Otherwise handle normally */
{
HChar name[30]; // large enough
SysRes sres;
- struct vki_open_how * how;
+ struct vki_open_how* how = (struct vki_open_how *)ARG3;
+ Bool proc_self_exe = False;
+ Bool can_deref_how = how && ML_(safe_to_deref)(how, sizeof(*how));
+
+ // ARG4 is supposed to be sizeof(struct vki_open_how)
+ // but we can't trust it
+ if (can_deref_how) {
+ // check that we are not trying to open the client exe for writing
+ // this doesn't handle all of the RESOLVE options, there may be cases
+ // like RESOLVE_NO_XDEV and RESOLVE_BENEATH where the path is
+ // invalid and we might return the wrong errno
+ if (how->vki_resolve != VKI_RESOLVE_IN_ROOT &&
+ how->vki_resolve != VKI_RESOLVE_NO_MAGICLINKS) {
+ /* Check for /proc/self/exe or /proc/<pid>/exe case
+ * first so that we can then use the later checks. */
+ VG_(sprintf)(name, "/proc/%d/exe", VG_(getpid)());
+ if (ML_(safe_to_deref)( (void*)(Addr)ARG2, 1 )
+ && (VG_(strcmp)((HChar *)(Addr)ARG2, name) == 0
+ || VG_(strcmp)((HChar *)(Addr)ARG2, "/proc/self/exe") == 0)) {
+ proc_self_exe = True;
+ }
+ }
+ }
PRINT("sys_openat2 ( %ld, %#" FMT_REGWORD "x(%s), %#" FMT_REGWORD "x, %ld )",
SARG1, ARG2, (HChar*)(Addr)ARG2, ARG3, SARG4);
PRE_REG_READ4(long, "openat2",
- int, dfd, const char *, filename, struct vki_open_how *, how, vki_size_t, size);
+ int, dirfd, const char *, pathname, struct vki_open_how *, how, vki_size_t, size);
- PRE_MEM_RASCIIZ( "openat2(filename)", ARG2 );
+ PRE_MEM_RASCIIZ( "openat2(pathname)", ARG2 );
PRE_MEM_READ( "openat2(how)", ARG3, sizeof(struct vki_open_how));
- /* For absolute filenames, dfd is ignored. If dfd is AT_FDCWD,
- filename is relative to cwd. When comparing dfd against AT_FDCWD,
+ if (can_deref_how &&
+ ((how->vki_flags & VKI_O_WRONLY) ||
+ (how->vki_flags & VKI_O_RDWR))) {
+ if (proc_self_exe) {
+ SET_STATUS_Failure( VKI_ETXTBSY );
+ return;
+ } else {
+ vg_assert(VG_(resolved_exename));
+ if (!VG_(strncmp)(VG_(resolved_exename), "#!", 2)) {
+ goto no_client_write;
+ }
+ Int fd = ARG1;
+ const HChar* path = (const HChar*)ARG2;
+ if (ML_(safe_to_deref)(path, 1)) {
+ // we need something like a "ML_(safe_to_deref_path)" that does a binary search for the addressable length, and maybe nul
+ if (path[0] == '/' && how->vki_resolve != VKI_RESOLVE_IN_ROOT) {
+ // absolute path, ignore fd
+ HChar tmp[VKI_PATH_MAX];
+ if (VG_(realpath)(path, tmp)) {
+ if (!VG_(strcmp)(tmp, VG_(resolved_exename))) {
+ SET_STATUS_Failure( VKI_ETXTBSY );
+ return;
+ }
+ }
+ } else {
+ // relative or rooted path
+ // or RESOLVE_IN_ROOT
+ if (how->vki_resolve == VKI_RESOLVE_IN_ROOT) {
+ while (*path == '/' && *path != '\0') {
+ ++path;
+ }
+ }
+ if (fd == VKI_AT_FDCWD) {
+ HChar tmp[VKI_PATH_MAX];
+ if (VG_(realpath)(path, tmp)) {
+ if (!VG_(strcmp)(tmp, VG_(resolved_exename))) {
+ SET_STATUS_Failure( VKI_ETXTBSY );
+ return;
+ }
+ }
+ } else {
+ // build absolute path from fd and path
+ const HChar* dirname;
+ if (VG_(resolve_filename)(fd, &dirname) == False) {
+ goto no_client_write; // let the OS do the error handling
+ }
+ HChar tmp1[VKI_PATH_MAX];
+ VG_(snprintf)(tmp1, VKI_PATH_MAX, "%s/%s", dirname, path);
+ tmp1[VKI_PATH_MAX - 1] = '\0';
+ HChar tmp2[VKI_PATH_MAX];
+ if (VG_(realpath)(tmp1, tmp2)) {
+ if (!VG_(strcmp)(tmp2, VG_(resolved_exename))) {
+ SET_STATUS_Failure( VKI_ETXTBSY );
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ no_client_write:
+
+ /* For absolute filenames, dirfd is ignored. If dirfd is AT_FDCWD,
+ filename is relative to cwd. When comparing dirfd against AT_FDCWD,
be sure only to compare the bottom 32 bits. */
if (ML_(safe_to_deref)( (void*)(Addr)ARG2, 1 )
&& *(Char *)(Addr)ARG2 != '/'
&& ((Int)ARG1) != ((Int)VKI_AT_FDCWD)
- && !ML_(fd_allowed)(ARG1, "openat2", tid, False))
+ && !ML_(fd_allowed)(ARG1, "openat", tid, False))
SET_STATUS_Failure( VKI_EBADF );
-
- how = (struct vki_open_how *)ARG3;
-
- if (how && ML_(safe_to_deref) (how, sizeof(struct vki_open_how))) {
- if (how->vki_mode) {
- if (!(how->vki_flags & ((vki_uint64_t)VKI_O_CREAT | VKI_O_TMPFILE))) {
- SET_STATUS_Failure( VKI_EINVAL );
- }
- }
- if (how->vki_resolve & ~((vki_uint64_t)VKI_RESOLVE_NO_XDEV |
- VKI_RESOLVE_NO_MAGICLINKS |
- VKI_RESOLVE_NO_SYMLINKS |
- VKI_RESOLVE_BENEATH |
- VKI_RESOLVE_IN_ROOT |
- VKI_RESOLVE_CACHED)) {
- SET_STATUS_Failure( VKI_EINVAL );
- }
- }
-
/* Handle the case where the open is of /proc/self/cmdline or
/proc/<pid>/cmdline, and just give it a copy of the fd for the
fake file we cooked up at startup (in m_main). Also, seek the
return;
}
- /* And for /proc/self/exe or /proc/<pid>/exe case. */
+ if (proc_self_exe) {
- VG_(sprintf)(name, "/proc/%d/exe", VG_(getpid)());
- if (ML_(safe_to_deref)( (void*)(Addr)ARG2, 1 )
- && (VG_(strcmp)((HChar *)(Addr)ARG2, name) == 0
- || VG_(strcmp)((HChar *)(Addr)ARG2, "/proc/self/exe") == 0)) {
- sres = VG_(dup)( VG_(cl_exec_fd) );
- SET_STATUS_from_SysRes( sres );
- if (!sr_isError(sres)) {
- OffT off = VG_(lseek)( sr_Res(sres), 0, VKI_SEEK_SET );
- if (off < 0)
- SET_STATUS_Failure( VKI_EMFILE );
- }
- return;
+ // do the syscall with VG_(resolved_exename)
+ SET_STATUS_from_SysRes(VG_(do_syscall4)(SYSNO, ARG1, (Word)VG_(resolved_exename), ARG3, ARG4));
}
/* Otherwise handle normally */
return if the working directory couldn't be found. */
extern void VG_(record_startup_wd) ( void );
-#if defined(VGO_freebsd)
+/* Resolves a path to a canonical absolute path */
extern Bool VG_(realpath)(const HChar *path, HChar *resolved);
-#endif
#endif // __PUB_CORE_LIBCFILE_H
extern Off64T VG_(lseek) ( Int fd, Off64T offset, Int whence );
extern SysRes VG_(stat) ( const HChar* file_name, struct vg_stat* buf );
-#if defined(VGO_freebsd)
extern SysRes VG_(lstat) ( const HChar* file_name, struct vg_stat* buf );
-#endif
extern Int VG_(fstat) ( Int fd, struct vg_stat* buf );
extern SysRes VG_(dup) ( Int oldfd );
extern SysRes VG_(dup2) ( Int oldfd, Int newfd );
-----------------------------------------------------
437: __NR_openat2 4s 2m
-----------------------------------------------------
-Syscall param openat2(dfd) contains uninitialised byte(s)
+Syscall param openat2(dirfd) contains uninitialised byte(s)
...
by 0x........: main (scalar_openat2.c:15)
-Syscall param openat2(filename) contains uninitialised byte(s)
+Syscall param openat2(pathname) contains uninitialised byte(s)
...
by 0x........: main (scalar_openat2.c:15)
...
by 0x........: main (scalar_openat2.c:15)
-Syscall param openat2(filename) points to unaddressable byte(s)
+Syscall param openat2(pathname) points to unaddressable byte(s)
...
by 0x........: main (scalar_openat2.c:15)
Address 0x........ is not stack'd, malloc'd or (recently) free'd
ksh_test.ksh \
ksh_test.stderr.exp \
ksh_test.stdout.exp \
+ open_client.vgtest \
+ open_client.stderr.exp \
sanity_level_thread.vgtest \
sanity_level_thread.stderr.exp \
swapcontext.vgtest \
usrstack.stdout.exp
check_PROGRAMS = \
- auxv bug452274 bug498317 bug499212 fexecve hello_world osrel \
+ auxv bug452274 bug498317 bug499212 fexecve hello_world open_client osrel \
proc_pid_file sanity_level_thread swapcontext umtx_shm_creat usrstack
AM_CFLAGS += $(AM_FLAG_M3264_PRI)
swapcontext_CFLAGS = ${AM_CFLAGS}
hello_world_SOURCES = hello_world.cpp
-
+open_client_SOURCES = open_client.cpp
proc_pid_file_SOURCES = proc_pid_file.cpp
sanity_level_thread_SOURCES = sanity_level_thread.cpp
--- /dev/null
+// For Bug 505673
+// Valgrind crashes with an internal error and SIGBUS when the guest tries to open its own file with O_WRONLY|O_CREAT|O_TRUNC
+#include <fcntl.h>
+#include <cerrno>
+#include <stdexcept>
+#include <vector>
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+int main(int argc, char** argv)
+{
+ std::vector<int> flags{O_WRONLY|O_CREAT|O_TRUNC, O_WRONLY, O_RDWR};
+
+ // On FreeBSD libc open uses syscall openat (at least on 14.2)
+ for (auto f : flags)
+ {
+ int res = open(argv[0], f, 0666);
+ if (-1 != res)
+ {
+ throw std::runtime_error("open should have failed");
+ }
+ else
+ {
+ if (errno != ETXTBSY)
+ {
+ throw std::runtime_error("errno should be ETXTBSY");
+ }
+ }
+ }
+
+ // repeat the above, but with syscall SYS_open
+ for (auto f : flags)
+ {
+ int res = syscall(SYS_open, argv[0], f, 0666);
+ if (-1 != res)
+ {
+ throw std::runtime_error("open should have failed");
+ }
+ else
+ {
+ if (errno != ETXTBSY)
+ {
+ throw std::runtime_error("errno should be ETXTBSY");
+ }
+ }
+ }
+
+ int dotdot;
+ if ((dotdot = open("..", O_DIRECTORY | O_RDONLY)) == -1)
+ {
+ throw std::runtime_error("failed to open ,.");
+ }
+ else
+ {
+ for (auto f : flags)
+ {
+ int res = openat(dotdot, "freebsd/open_client", f, 0666);
+ if (-1 != res)
+ {
+ throw std::runtime_error("open should have failed");
+ }
+ else
+ {
+ if (errno != ETXTBSY)
+ {
+ throw std::runtime_error("errno should be ETXTBSY");
+ }
+ }
+ }
+ }
+ close(dotdot);
+
+ chdir("..");
+
+ // check that relative paths work
+ for (auto f : flags)
+ {
+ int res = open("freebsd/open_client", f, 0666);
+ if (-1 != res)
+ {
+ throw std::runtime_error("open should have failed");
+ }
+ else
+ {
+ if (errno != ETXTBSY)
+ {
+ throw std::runtime_error("errno should be ETXTBSY");
+ }
+ }
+ }
+
+ for (auto f : flags)
+ {
+ int res = syscall(SYS_open, "freebsd/open_client", f, 0666);
+ if (-1 != res)
+ {
+ throw std::runtime_error("open should have failed");
+ }
+ else
+ {
+ if (errno != ETXTBSY)
+ {
+ throw std::runtime_error("errno should be ETXTBSY");
+ }
+ }
+ }
+}
--- /dev/null
+prog: open_client
+vgopts: -q
+
mremap4.stderr.exp mremap4.vgtest \
mremap5.stderr.exp mremap5.vgtest \
mremap6.stderr.exp mremap6.vgtest \
+ open_client.stderr.exp open_client.vgtest \
pthread-stack.stderr.exp pthread-stack.vgtest \
stack-overflow.stderr.exp stack-overflow.vgtest
mremap4 \
mremap5 \
mremap6 \
+ open_client \
pthread-stack \
stack-overflow
# Special needs
clonev_LDADD = -lpthread
+open_client_SOURCES = open_client.cpp
pthread_stack_LDADD = -lpthread
stack_overflow_CFLAGS = $(AM_CFLAGS) @FLAG_W_NO_UNINITIALIZED@ \
--- /dev/null
+// For Bug 505673
+// Valgrind crashes with an internal error and SIGBUS when the guest tries to open its own file with O_WRONLY|O_CREAT|O_TRUNC
+#include <fcntl.h>
+#include <cerrno>
+#include <stdexcept>
+#include <string>
+#include <vector>
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <linux/openat2.h>
+
+int main(int argc, char** argv)
+{
+ std::vector<__u64> flags{O_WRONLY|O_CREAT|O_TRUNC, O_WRONLY, O_RDWR};
+ auto pid = getpid();
+ auto ppe = std::string("/proc/") + std::to_string(pid) + "/exe";
+ std::vector<std::string> names{argv[0], "/proc/self/exe", ppe};
+ int dotdot;
+
+ for (const auto& n : names)
+ {
+ for (auto f : flags)
+ {
+ int res = open(n.c_str(), f, 0666);
+ if (-1 != res)
+ {
+ throw std::runtime_error("open should have failed");
+ }
+ else
+ {
+ if (errno != ETXTBSY)
+ {
+ throw std::runtime_error("errno should be ETXTBSY");
+ }
+ }
+ }
+
+ for (auto f : flags)
+ {
+ int res = syscall(SYS_open, n.c_str(), f, 0666);
+
+ if (-1 != res)
+ {
+ throw std::runtime_error("open should have failed");
+ }
+ else
+ {
+ if (errno != ETXTBSY)
+ {
+ throw std::runtime_error("errno should be ETXTBSY");
+ }
+ }
+ }
+ }
+
+ if ((dotdot = open("..", O_DIRECTORY | O_RDONLY)) == -1)
+ {
+ throw std::runtime_error("failed to open ,.");
+ }
+ else
+ {
+ for (auto f : flags)
+ {
+ int res = openat(dotdot, "linux/open_client", f, 0666);
+ if (-1 != res)
+ {
+ throw std::runtime_error("open should have failed");
+ }
+ else
+ {
+ if (errno != ETXTBSY)
+ {
+ throw std::runtime_error("errno should be ETXTBSY");
+ }
+ }
+ }
+ }
+
+#if defined(SYS_openat2)
+ for (const auto& n : names)
+ {
+ for (auto f : flags)
+ {
+ struct open_how oh = { .flags=f, .mode=((f&static_cast<__u64>(O_CREAT))?0666UL:0UL), .resolve=0 };
+ int res = syscall(SYS_openat2, AT_FDCWD, n.c_str(), &oh, sizeof(oh));
+ if (-1 != res)
+ {
+ throw std::runtime_error("openat2 should have failed");
+ }
+ else
+ {
+ if (errno != ETXTBSY)
+ {
+ throw std::runtime_error("errno should be ETXTBSY");
+ }
+ }
+ }
+ }
+
+ for (auto f : flags)
+ {
+ struct open_how oh = { .flags=f, .mode=((f&static_cast<__u64>(O_CREAT))?0666UL:0UL), .resolve=0 };
+ int res = syscall(SYS_openat2, dotdot, "linux/open_client", &oh, sizeof(oh));
+ if (-1 != res)
+ {
+ throw std::runtime_error("openat2 should have failed");
+ }
+ else
+ {
+ if (errno != ETXTBSY)
+ {
+ throw std::runtime_error("errno should be ETXTBSY");
+ }
+ }
+ }
+
+ for (auto f : flags)
+ {
+ struct open_how oh = { .flags=f, .mode=((f&static_cast<__u64>(O_CREAT))?0666UL:0UL), .resolve=RESOLVE_IN_ROOT };
+ int res = syscall(SYS_openat2, dotdot, "/linux/open_client", &oh, sizeof(oh));
+ if (-1 != res)
+ {
+ throw std::runtime_error("openat2 should have failed");
+ }
+ else
+ {
+ if (errno != ETXTBSY)
+ {
+ throw std::runtime_error("errno should be ETXTBSY");
+ }
+ }
+ }
+#endif
+
+ close(dotdot);
+
+ chdir("..");
+
+ // check that relative paths work
+ for (auto f : flags)
+ {
+ int res = open("linux/open_client", f, 0666);
+ if (-1 != res)
+ {
+ throw std::runtime_error("open should have failed");
+ }
+ else
+ {
+ if (errno != ETXTBSY)
+ {
+ throw std::runtime_error("errno should be ETXTBSY");
+ }
+ }
+ }
+
+ for (auto f : flags)
+ {
+ int res = syscall(SYS_open, "linux/open_client", f, 0666);
+ if (-1 != res)
+ {
+ throw std::runtime_error("open should have failed");
+ }
+ else
+ {
+ if (errno != ETXTBSY)
+ {
+ throw std::runtime_error("errno should be ETXTBSY");
+ }
+ }
+ }
+
+#if defined(SYS_openat2)
+ for (auto f : flags)
+ {
+ struct open_how oh = { .flags=f, .mode=((f&static_cast<__u64>(O_CREAT))?0666UL:0UL), .resolve=0 };
+ int res = syscall(SYS_openat2, AT_FDCWD, "linux/open_client", &oh, sizeof(oh));
+ if (-1 != res)
+ {
+ throw std::runtime_error("openat2 should have failed");
+ }
+ else
+ {
+ if (errno != ETXTBSY)
+ {
+ throw std::runtime_error("errno should be ETXTBSY");
+ }
+ }
+ }
+#endif
+}
--- /dev/null
+prog: open_client
+vgopts: -q