diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index edcad897d..b0f06350c 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,32 @@ +2005-07-16 Christopher Faylor + + * child_info.h (child_info::sync): Pass pid and HANDLE rather than + using pinfo. + (child_info::child_info): Accept an argument controlling whether to + create proc_subproc. + (child_info_spawn::child_info_spawn): Ditto. + * sigproc.cc (child_info::child_info): Ditto. + (child_info_spawn::child_info_spawn): Ditto. + (child_info::sync): Use passed in pid and HANDLE. + * fork.cc (fork_parent): Reflect additional arguments required for + child_info::sync. + * hookapi.cc (hook_or_detect_cygwin): Rename. Change so that NULL 'fn' + argument just returns "true", indicating that program uses cygwin1.dll. + * spawn.cc (av::win16_exe): New element. + * spawn.cc (av::iscygwin): New element. + (av::fixup): New function. + (spawn_guts): Protect against SEGV. Use fixup function to detect when + it is safe to wait for a spawned (as opposed to an execed) program. + Reflect changes in child_info::sync arguments. + * external.cc (cygwin_internal): Reflect function renaming to + hook_or_detect_cygwin. + + * cygheap.cc (cygheap_fixup_in_child): Close handle after debug fixup + has been done to prevent false positives in handle collision. + + * exceptions.cc (try_to_debug): Notify debugger if already being + debugged. + 2005-07-09 Christopher Faylor * path.cc (mount): Only check win32_path when we know we need it. @@ -47,6 +76,7 @@ (handler_dev_raw::close): Ditto. (fhandler_dev_clipboard::fixup_after_exec): New method. * fhandler_dev_mem.cc (fhandler_dev_mem::close): Eliminate pass through + function in favor of virtual method. * fhandler_dev_raw.cc (fhandler_dev_raw::close): Ditto. * fhandler_clipboard.cc (fhandler_dev_clipboard::close): Don't go to extra effort when execing. @@ -55,8 +85,8 @@ when we know we're execing. * fhandler_disk_file.cc (fhandler_disk_file::close): Ditto. * fhandler_dsp.cc (fhandler_dev_dsp::close): Ditto. - * fhandler_fifo.cc (fhandler_fifo.cc::close): Ditto. - function in favor of base function. + * fhandler_fifo.cc (fhandler_fifo.cc::close): Ditto. function in favor + of base function. * fhandler_random.cc (fhandler_dev_random::close): Ditto. * fhandler_registry.cc (fhandler_registry::close): Ditto. * fhandler_tty.cc (fhandler_tty_slave::close): Ditto. diff --git a/winsup/cygwin/child_info.h b/winsup/cygwin/child_info.h index 5e2bc0578..c079fe226 100644 --- a/winsup/cygwin/child_info.h +++ b/winsup/cygwin/child_info.h @@ -29,7 +29,7 @@ enum child_info_types #define EXEC_MAGIC_SIZE sizeof(child_info) -#define CURR_CHILD_INFO_MAGIC 0xd94c588aU +#define CURR_CHILD_INFO_MAGIC 0x5eecb012U /* NOTE: Do not make gratuitous changes to the names or organization of the below class. The layout is checksummed to determine compatibility between @@ -50,10 +50,10 @@ public: DWORD cygheap_reserve_sz; DWORD dwProcessId; unsigned fhandler_union_cb; - child_info (unsigned, child_info_types); + child_info (unsigned, child_info_types, bool); ~child_info (); void ready (bool); - bool sync (pinfo&, DWORD); + bool sync (int, HANDLE, DWORD) __attribute__ ((regparm (3))); }; class mount_info; @@ -104,7 +104,7 @@ public: cfree (moreinfo); } } - child_info_spawn (child_info_types); + child_info_spawn (child_info_types, bool); }; void __stdcall init_child_info (DWORD, child_info *, HANDLE); diff --git a/winsup/cygwin/cygheap.cc b/winsup/cygwin/cygheap.cc index 633df7f09..9f2f7d4b3 100644 --- a/winsup/cygwin/cygheap.cc +++ b/winsup/cygwin/cygheap.cc @@ -51,7 +51,7 @@ extern "C" { static void __stdcall _cfree (void *) __attribute__((regparm(1))); static void *__stdcall _csbrk (int); } - + /* Called by fork or spawn to reallocate cygwin heap */ void __stdcall cygheap_fixup_in_child (bool execed) @@ -60,13 +60,16 @@ cygheap_fixup_in_child (bool execed) cygheap = (init_cygheap *) cygheap_max; _csbrk ((char *) child_proc_info->cygheap_max - (char *) cygheap); child_copy (child_proc_info->parent, child_proc_info->dwProcessId, "cygheap", cygheap, cygheap_max); + cygheap_init (); + debug_fixup_after_fork_exec (); + + /* Need to do this after debug_fixup_after_fork_exec or DEBUGGING handling of + handles might get confused. */ if (execed) { CloseHandle (child_proc_info->parent); child_proc_info->parent = NULL; } - cygheap_init (); - debug_fixup_after_fork_exec (); if (execed) { diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc index 4f9a55e3d..1d3d8def7 100644 --- a/winsup/cygwin/exceptions.cc +++ b/winsup/cygwin/exceptions.cc @@ -320,8 +320,13 @@ extern "C" int try_to_debug (bool waitloop) { debug_printf ("debugger_command '%s'", debugger_command); - if (*debugger_command == '\0' || being_debugged ()) + if (*debugger_command == '\0') return 0; + if (being_debugged ()) + { + DebugBreak (); + return 0; + } __small_sprintf (strchr (debugger_command, '\0'), " %u", GetCurrentProcessId ()); diff --git a/winsup/cygwin/external.cc b/winsup/cygwin/external.cc index a40ef1ed7..5e8153682 100644 --- a/winsup/cygwin/external.cc +++ b/winsup/cygwin/external.cc @@ -29,7 +29,6 @@ details. */ #include "cygtls.h" #include "child_info.h" -void *hook_cygwin (const char *, const void *); child_info *get_cygwin_startup_info (); static winpids pids; @@ -295,7 +294,7 @@ cygwin_internal (cygwin_getinfo_types t, ...) { const char *name = va_arg (arg, const char *); const void *hookfn = va_arg (arg, const void *); - return (unsigned long) hook_cygwin (name, hookfn); + return (unsigned long) hook_or_detect_cygwin (name, hookfn); } case CW_ARGV: { diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc index e3b606607..649baa3e3 100644 --- a/winsup/cygwin/fork.cc +++ b/winsup/cygwin/fork.cc @@ -414,7 +414,7 @@ fork_parent (HANDLE&, dll *&first_dll, bool& load_dlls, void *stack_here, child_ #endif /* Wait for subproc to initialize itself. */ - if (!ch.sync (child, FORK_WAIT_TIMEOUT)) + if (!ch.sync (child->pid, pi.hProcess, FORK_WAIT_TIMEOUT)) { system_printf ("child %d died waiting for longjmp before initialization", child_pid); goto cleanup; @@ -465,7 +465,7 @@ fork_parent (HANDLE&, dll *&first_dll, bool& load_dlls, void *stack_here, child_ /* Start thread, and wait for it to reload dlls. */ if (!resume_child (forker_finished)) goto cleanup; - else if (!ch.sync (child, FORK_WAIT_TIMEOUT)) + else if (!ch.sync (child->pid, pi.hProcess, FORK_WAIT_TIMEOUT)) { system_printf ("child %d died waiting for dll loading", child_pid); goto cleanup; diff --git a/winsup/cygwin/hookapi.cc b/winsup/cygwin/hookapi.cc index 6542a6ebe..97e3330ee 100644 --- a/winsup/cygwin/hookapi.cc +++ b/winsup/cygwin/hookapi.cc @@ -150,7 +150,7 @@ makename (const char *name, char *&buf, int& i, int inc) // Top level routine to find the EXE's imports, and redirect them void * -hook_cygwin (const char *name, const void *fn) +hook_or_detect_cygwin (const char *name, const void *fn) { HMODULE hm = GetModuleHandle (NULL); PIMAGE_NT_HEADERS pExeNTHdr = PEHeaderFromHModule (hm); @@ -170,16 +170,19 @@ hook_cygwin (const char *name, const void *fn) fh.origfn = NULL; fh.hookfn = fn; char *buf = (char *) alloca (strlen (name) + strlen ("64") + sizeof ("_")); - int i = -1; - while (!fh.origfn && (fh.name = makename (name, buf, i, 1))) + int i; + // Iterate through each import descriptor, and redirect if appropriate + for (PIMAGE_IMPORT_DESCRIPTOR pd = pdfirst; pd->FirstThunk; pd++) { - // Iterate through each import descriptor, and redirect if appropriate - for (PIMAGE_IMPORT_DESCRIPTOR pd = pdfirst; pd->FirstThunk; pd++) - { - PSTR modname = rva (PSTR, hm, pd->Name); - if (strcasematch (modname, "cygwin1.dll")) - RedirectIAT (fh, pd, hm); - } + if (!strcasematch (rva (PSTR, hm, pd->Name), "cygwin1.dll")) + continue; + if (!fn) + return (void *) "found it"; // just checking if executable used cygwin1.dll + i = -1; + while (!fh.origfn && (fh.name = makename (name, buf, i, 1))) + RedirectIAT (fh, pd, hm); + if (fh.origfn) + break; } while (!fh.origfn && (fh.name = makename (name, buf, i, -1))) diff --git a/winsup/cygwin/sigproc.cc b/winsup/cygwin/sigproc.cc index d85064b18..2bb9d4eda 100644 --- a/winsup/cygwin/sigproc.cc +++ b/winsup/cygwin/sigproc.cc @@ -721,7 +721,7 @@ out: /* Initialize some of the memory block passed to child processes by fork/spawn/exec. */ -child_info::child_info (unsigned in_cb, child_info_types chtype) +child_info::child_info (unsigned in_cb, child_info_types chtype, bool need_subproc_ready) { memset (this, 0, in_cb); cb = in_cb; @@ -730,7 +730,7 @@ child_info::child_info (unsigned in_cb, child_info_types chtype) type = chtype; fhandler_union_cb = sizeof (fhandler_union); user_h = cygwin_user_h; - if (chtype != PROC_SPAWN) + if (need_subproc_ready) subproc_ready = CreateEvent (&sec_all, FALSE, FALSE, NULL); sigproc_printf ("subproc_ready %p", subproc_ready); cygheap = ::cygheap; @@ -753,12 +753,12 @@ child_info::~child_info () } child_info_fork::child_info_fork () : - child_info (sizeof *this, _PROC_FORK) + child_info (sizeof *this, _PROC_FORK, true) { } -child_info_spawn::child_info_spawn (child_info_types chtype) : - child_info (sizeof *this, chtype) +child_info_spawn::child_info_spawn (child_info_types chtype, bool need_subproc_ready) : + child_info (sizeof *this, chtype, need_subproc_ready) { } @@ -786,7 +786,7 @@ child_info::ready (bool execed) } bool -child_info::sync (pinfo& vchild, DWORD howlong) +child_info::sync (pid_t pid, HANDLE hProcess, DWORD howlong) { if (!subproc_ready) { @@ -796,14 +796,14 @@ child_info::sync (pinfo& vchild, DWORD howlong) HANDLE w4[2]; w4[0] = subproc_ready; - w4[1] = vchild.hProcess; + w4[1] = hProcess; bool res; sigproc_printf ("waiting for subproc_ready(%p) and child process(%p)", w4[0], w4[1]); switch (WaitForMultipleObjects (2, w4, FALSE, howlong)) { case WAIT_OBJECT_0: - sigproc_printf ("got subproc_ready for pid %d", vchild->pid); + sigproc_printf ("got subproc_ready for pid %d", pid); res = true; break; case WAIT_OBJECT_0 + 1: @@ -813,7 +813,7 @@ child_info::sync (pinfo& vchild, DWORD howlong) res = false; break; default: - system_printf ("wait failed, pid %d, %E", vchild->pid); + system_printf ("wait failed, pid %d, %E", pid); res = false; break; } diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc index cf6d3e4fe..8261a45da 100644 --- a/winsup/cygwin/spawn.cc +++ b/winsup/cygwin/spawn.cc @@ -32,7 +32,7 @@ details. */ #include "pinfo.h" #include "registry.h" #include "environ.h" -#include "cygthread.h" +#include "cygtls.h" #define LINE_BUF_CHUNK (CYG_MAX_PATH * 2) @@ -264,7 +264,9 @@ class av public: int error; int argc; - av (int ac, const char * const *av) : calloced (0), error (false), argc (ac) + bool win16_exe; + bool iscygwin; + av (int ac, const char * const *av) : calloced (0), error (false), argc (ac), win16_exe (false), iscygwin (true) { argv = (char **) cmalloc (HEAP_1_ARGV, (argc + 5) * sizeof (char *)); memcpy (argv, av, (argc + 1) * sizeof (char *)); @@ -303,6 +305,7 @@ class av if (!(argv[i] = cstrdup1 (argv[i]))) error = errno; } + int fixup (child_info_types, const char *, path_conv&, const char *); }; int @@ -360,8 +363,6 @@ spawn_guts (const char * prog_arg, const char *const *argv, bool rc; pid_t cygpid; - MALLOC_CHECK; - if (prog_arg == NULL) { syscall_printf ("prog_arg is NULL"); @@ -400,9 +401,12 @@ spawn_guts (const char * prog_arg, const char *const *argv, for (ac = 0; argv[ac]; ac++) /* nothing */; + myfault efault; + if (efault.faulted (EFAULT)) + return -1; // FIXME: Could be very leaky + av newargv (ac, argv); - bool win16_exe = false; int null_app_name = 0; if (ac == 3 && argv[1][0] == '/' && argv[1][1] == 'c' && (iscmd (argv[0], "command.com") || iscmd (argv[0], "cmd.exe"))) @@ -431,100 +435,10 @@ spawn_guts (const char * prog_arg, const char *const *argv, } MALLOC_CHECK; - - /* If the file name ends in either .exe, .com, .bat, or .cmd we assume - that it is NOT a script file */ - while (*ext == '\0' || (wincap.detect_win16_exe () && strcasematch (ext, ".exe"))) - { - HANDLE hnd = CreateFile (real_path, GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE, - &sec_none_nih, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, 0); - if (hnd == INVALID_HANDLE_VALUE) - { - __seterrno (); - return -1; - } - - DWORD done; - - char buf[2 * CYG_MAX_PATH]; - buf[0] = buf[1] = buf[2] = buf[sizeof (buf) - 1] = '\0'; - if (!ReadFile (hnd, buf, sizeof (buf) - 1, &done, 0)) - { - CloseHandle (hnd); - __seterrno (); - return -1; - } - - CloseHandle (hnd); - - if (buf[0] == 'M' && buf[1] == 'Z') - { - unsigned off = (unsigned char) buf[0x18] | (((unsigned char) buf[0x19]) << 8); - win16_exe = off < sizeof (IMAGE_DOS_HEADER); - break; - } - - debug_printf ("%s is a script", (char *) real_path); - - if (real_path.has_acls () && allow_ntsec - && check_file_access (real_path, X_OK)) - { - debug_printf ("... but not executable"); - break; - } - - char *pgm, *arg1; - - if (buf[0] != '#' || buf[1] != '!') - { - pgm = (char *) "/bin/sh"; - arg1 = NULL; - } - else - { - char *ptr; - pgm = buf + 2; - pgm += strspn (pgm, " \t"); - for (ptr = pgm, arg1 = NULL; - *ptr && *ptr != '\r' && *ptr != '\n'; - ptr++) - if (!arg1 && (*ptr == ' ' || *ptr == '\t')) - { - /* Null terminate the initial command and step over - any additional white space. If we've hit the - end of the line, exit the loop. Otherwise, we've - found the first argument. Position the current - pointer on the last known white space. */ - *ptr = '\0'; - char *newptr = ptr + 1; - newptr += strspn (newptr, " \t"); - if (!*newptr || *newptr == '\r' || *newptr == '\n') - break; - arg1 = newptr; - ptr = newptr - 1; - } - - *ptr = '\0'; - } - - /* Replace argv[0] with the full path to the script if this is the - first time through the loop. */ - newargv.replace0_maybe (prog_arg); - - /* pointers: - * pgm interpreter name - * arg1 optional string - */ - if (arg1) - newargv.unshift (arg1); - - /* FIXME: This should not be using FE_NATIVE. It should be putting - the posix path on the argv list. */ - find_exec (pgm, real_path, "PATH=", FE_NATIVE, &ext); - newargv.unshift (real_path, 1); - } + int res; + res = newargv.fixup (chtype, prog_arg, real_path, ext); + if (res) + return res; if (real_path.iscygexec ()) newargv.dup_all (); @@ -672,7 +586,7 @@ spawn_guts (const char * prog_arg, const char *const *argv, cygheap->user.deimpersonate (); moreinfo->envp = build_env (envp, envblock, moreinfo->envc, real_path.iscygexec ()); - child_info_spawn ciresrv (chtype); + child_info_spawn ciresrv (chtype, newargv.iscygwin); ciresrv.moreinfo = moreinfo; si.lpReserved2 = (LPBYTE) &ciresrv; @@ -763,7 +677,6 @@ spawn_guts (const char * prog_arg, const char *const *argv, /* FIXME: There is a small race here */ - int res; pthread_cleanup cleanup; if (mode == _P_SYSTEM) { @@ -794,6 +707,7 @@ spawn_guts (const char * prog_arg, const char *const *argv, ProtectHandle1 (pi.hProcess, childhProc); bool synced; + pid_t pid; if (mode == _P_OVERLAY) { myself->dwProcessId = dwExeced = pi.dwProcessId; @@ -813,7 +727,7 @@ spawn_guts (const char * prog_arg, const char *const *argv, on this fact when we exit. dup_proc_pipe will close our end of the pipe. Note that wr_proc_pipe may also be == INVALID_HANDLE_VALUE. That will make dup_proc_pipe essentially a no-op. */ - if (!win16_exe && myself->wr_proc_pipe) + if (!newargv.win16_exe && myself->wr_proc_pipe) { myself->sync_proc_pipe (); /* Make sure that we own wr_proc_pipe just in case we've been previously @@ -821,6 +735,7 @@ spawn_guts (const char * prog_arg, const char *const *argv, myself.zap_cwd (); myself->dup_proc_pipe (pi.hProcess); } + pid = myself->pid; } else { @@ -856,6 +771,7 @@ spawn_guts (const char * prog_arg, const char *const *argv, res = -1; goto out; } + pid = child->pid; } /* Start the child running */ @@ -865,7 +781,7 @@ spawn_guts (const char * prog_arg, const char *const *argv, sigproc_printf ("spawned windows pid %d", pi.dwProcessId); - synced = ciresrv.sync (myself, INFINITE); + synced = ciresrv.sync (pid, pi.hProcess, INFINITE); switch (mode) { @@ -1075,3 +991,104 @@ spawnvpe (int mode, const char *file, const char * const *argv, path_conv buf; return spawnve (mode, find_exec (file, buf), argv, envp); } + +int +av::fixup (child_info_types chtype, const char *prog_arg, path_conv& real_path, const char *ext) +{ + /* If the file name ends in either .exe, .com, .bat, or .cmd we assume + that it is NOT a script file */ + while (*ext == '\0' || chtype == PROC_SPAWN || (wincap.detect_win16_exe () && strcasematch (ext, ".exe"))) + { + HANDLE h = CreateFile (real_path, GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + &sec_none_nih, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, 0); + if (h == INVALID_HANDLE_VALUE) + goto err; + + HANDLE hm = CreateFileMapping (h, &sec_none_nih, PAGE_READONLY, 0, 0, NULL); + CloseHandle (h); + if (!hm) + goto err; + char *buf = (char *) MapViewOfFile(hm, FILE_MAP_READ, 0, 0, 0); + CloseHandle (hm); + if (!buf) + goto err; + + if (buf[0] == 'M' && buf[1] == 'Z') + { + unsigned off = (unsigned char) buf[0x18] | (((unsigned char) buf[0x19]) << 8); + win16_exe = off < sizeof (IMAGE_DOS_HEADER); + if (!win16_exe) + iscygwin = hook_or_detect_cygwin (buf, NULL); + UnmapViewOfFile (buf); + break; + } + + debug_printf ("%s is a script", (char *) real_path); + + if (real_path.has_acls () && allow_ntsec + && check_file_access (real_path, X_OK)) + { + debug_printf ("... but not executable"); + break; + } + + char *pgm = NULL; + char *arg1 = NULL; + char *ptr = buf; + if (*ptr++ == '#' && *ptr++ == '!') + { + ptr += strspn (ptr, " \t"); + size_t len = strcspn (ptr, "\r\n"); + if (len) + { + char *namebuf = (char *) alloca (len + 1); + memcpy (namebuf, ptr, len); + namebuf[len] = '\0'; + for (ptr = pgm = namebuf; *ptr; ptr++) + if (!arg1 && (*ptr == ' ' || *ptr == '\t')) + { + /* Null terminate the initial command and step over any additional white + space. If we've hit the end of the line, exit the loop. Otherwise, + we've found the first argument. Position the current pointer on the + last known white space. */ + *ptr = '\0'; + char *newptr = ptr + 1; + newptr += strspn (newptr, " \t"); + if (!*newptr) + break; + arg1 = newptr; + ptr = newptr - 1; + } + } + } + UnmapViewOfFile (buf); + if (!pgm) + { + pgm = (char *) "/bin/sh"; + arg1 = NULL; + } + + /* Replace argv[0] with the full path to the script if this is the + first time through the loop. */ + replace0_maybe (prog_arg); + + /* pointers: + * pgm interpreter name + * arg1 optional string + */ + if (arg1) + unshift (arg1); + + /* FIXME: This should not be using FE_NATIVE. It should be putting + the posix path on the argv list. */ + find_exec (pgm, real_path, "PATH=", FE_NATIVE, &ext); + unshift (real_path, 1); + } + return 0; + +err: + __seterrno (); + return -1; +} diff --git a/winsup/cygwin/winsup.h b/winsup/cygwin/winsup.h index 2178d277f..3c289685b 100644 --- a/winsup/cygwin/winsup.h +++ b/winsup/cygwin/winsup.h @@ -255,6 +255,8 @@ extern "C" int __stdcall strcasematch (const char *s1, const char *s2) __attribu extern "C" int __stdcall strncasematch (const char *s1, const char *s2, size_t n) __attribute__ ((regparm(3))); extern "C" char *__stdcall strcasestr (const char *searchee, const char *lookfor) __attribute__ ((regparm(2))); +void *hook_or_detect_cygwin (const char *, const void *) __attribute__ ((regparm (2))); + /* Time related */ void __stdcall totimeval (struct timeval *, FILETIME *, int, int); long __stdcall to_time_t (FILETIME *);