diff options
-rw-r--r-- | winsup/cygwin/ChangeLog | 51 | ||||
-rw-r--r-- | winsup/cygwin/cygthread.cc | 61 | ||||
-rw-r--r-- | winsup/cygwin/cygthread.h | 2 | ||||
-rw-r--r-- | winsup/cygwin/debug.cc | 21 | ||||
-rw-r--r-- | winsup/cygwin/debug.h | 3 | ||||
-rw-r--r-- | winsup/cygwin/fhandler_tty.cc | 6 | ||||
-rw-r--r-- | winsup/cygwin/grp.cc | 4 | ||||
-rw-r--r-- | winsup/cygwin/how-signals-work.txt | 132 | ||||
-rw-r--r-- | winsup/cygwin/security.cc | 7 | ||||
-rw-r--r-- | winsup/cygwin/sigproc.cc | 7 | ||||
-rw-r--r-- | winsup/cygwin/spawn.cc | 1 | ||||
-rw-r--r-- | winsup/cygwin/syscalls.cc | 5 |
12 files changed, 256 insertions, 44 deletions
diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 216d83b9b6d..20f4c1262b0 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,54 @@ +2002-08-06 Christopher Faylor <cgf@redhat.com> + + * spawn.cc (spawn_guts): Don't set mount_h here. + * sigproc.cc (init_child_info): Set it here instead. + * shared.cc (cygwin_mount_h): Make NO_COPY. + +2002-08-06 Christopher Faylor <cgf@redhat.com> + + * cygthread.cc (cygthread::stub): Accept flag to pass info structure to + thread function. + (cygthread::operator new): Add defense debugging output. + (cygthread::cygthread): Add debugging output. Set name after thread + has been awakened to avoid a race. + (cygthread::exit_thread): Use handle operator rather than using ev + directly. + (cygthread::exit_thread): Reorganize to provide debugging. Set __name + to NULL. + * cygthread.h (cygself): Define. + * fhandler_tty.cc (fhandler_tty_master::init): Use cygself as argument + so that invoked thread can access its own info. + (process_output): Derive cygthread info of thread from thread argument. + * sigproc.cc (sigproc_init): Use cygself as argument so that invoked + thread can access its own info. + (wait_sig): Derive cygthread info of thread from thread argument. + +2002-08-06 Conrad Scott <conrad.scott@dsl.pipex.com> + + * debug.h (handle_list::allocated): Remove field. + * debug.cc (newh): Don't malloc extra entries. + (add_handle): Downgrade strace message level. + (delete_handle): Remove case for `allocated' entries. + +2002-08-05 Christopher Faylor <cgf@redhat.com> + + * cygthread.cc (cygthread::stub): Change event creation to manual + reset. Set __name after calling SetEvent to prevent races. + (cygthread::detach): Always reset event here to prevent races. + +2002-08-03 Conrad Scott <conrad.scott@dsl.pipex.com> + + * debug.h (WaitForMultipleObjects): Correct typo. + +2002-08-01 Pierre Humblet <Pierre.Humblet@ieee.org> + + * security.cc (verify_token): Do not reject a token just because + the supplementary group list is missing Everyone or a groupsid + equal to usersid, or because the primary group is not in the token, + as long as it is equal to the usersid. + * syscalls.cc (seteuid32): Use common code for all successful returns. + * grp.cc (getgroups32): Never includes Everyone in the output. + 2002-08-01 Christopher Faylor <cgf@redhat.com> * cygthread.cc (cygthread::exit_thread): Define new method. diff --git a/winsup/cygwin/cygthread.cc b/winsup/cygwin/cygthread.cc index a4f32472de1..00eeb25d07a 100644 --- a/winsup/cygwin/cygthread.cc +++ b/winsup/cygwin/cygthread.cc @@ -7,10 +7,10 @@ Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ #include "winsup.h" +#include <windows.h> #include "exceptions.h" #include "security.h" #include "cygthread.h" -#include <windows.h> #undef CloseHandle @@ -21,8 +21,9 @@ static HANDLE NO_COPY hthreads[NTHREADS]; DWORD NO_COPY cygthread::main_thread_id; -/* Initial stub called by makethread. Performs initial per-thread - initialization. */ +/* Initial stub called by cygthread constructor. Performs initial + per-thread initialization and loops waiting for new thread functions + to execute. */ DWORD WINAPI cygthread::stub (VOID *arg) { @@ -34,21 +35,27 @@ cygthread::stub (VOID *arg) init_exceptions (&except_entry); cygthread *info = (cygthread *) arg; - info->ev = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL); + info->ev = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL); while (1) { if (!info->func) ExitThread (0); - /* Cygwin threads should not call ExitThread */ - info->func (info->arg); + /* Cygwin threads should not call ExitThread directly */ + info->func (info->arg == cygself ? info : info->arg); + /* ...so the above should always return */ - info->__name = NULL; +#ifdef DEBUGGING + info->func = NULL; // catch erroneous activation +#endif SetEvent (info->ev); + info->__name = NULL; SuspendThread (info->h); } } +/* This function runs in a secondary thread and starts up a bunch of + other suspended threads for use in the cygthread pool. */ DWORD WINAPI cygthread::runner (VOID *arg) { @@ -59,6 +66,7 @@ cygthread::runner (VOID *arg) return 0; } +/* Start things going. Called from dll_crt0_1. */ void cygthread::init () { @@ -86,7 +94,7 @@ void * cygthread::operator new (size_t) { DWORD id; - cygthread *info; /* Various information needed by the newly created thread */ + cygthread *info; for (;;) { @@ -95,6 +103,10 @@ new (size_t) if ((id = (DWORD) InterlockedExchange ((LPLONG) &info->avail, 0))) { info->id = id; +#ifdef DEBUGGING + if (info->__name) + api_fatal ("name not NULL? id %p, i %d", id, info - threads); +#endif return info; } @@ -104,10 +116,18 @@ new (size_t) } cygthread::cygthread (LPTHREAD_START_ROUTINE start, LPVOID param, - const char *name): __name (name), func (start), arg (param) + const char *name): func (start), arg (param) { +#ifdef DEBUGGGING + if (!__name) + api_fatal ("name should never be NULL"); +#endif + thread_printf ("name %s, id %p", name, id); while (ResumeThread (h) == 0) Sleep (0); + __name = name; /* Need to set after thread has woken up to + ensure that it won't be cleared by exiting + thread. */ } /* Return the symbolic name of the current thread for debugging. @@ -147,25 +167,40 @@ HANDLE () return ev; } +/* Should only be called when the process is exiting since it + leaves an open thread slot. */ void cygthread::exit_thread () { - SetEvent (ev); + SetEvent (*this); ExitThread (0); } +/* Detach the cygthread from the current thread. Note that the + theory is that cygthread's are only associated with one thread. + So, there should be no problems with multiple threads doing waits + on the one cygthread. */ void cygthread::detach () { - if (!avail) + if (avail) + system_printf ("called detach on available thread %d?", avail); + else { DWORD avail = id; - if (__name) + /* Checking for __name here is just a minor optimization to avoid + an OS call. */ + if (!__name) + thread_printf ("thread id %p returned. No need to wait.", id); + else { DWORD res = WaitForSingleObject (*this, INFINITE); - debug_printf ("WFSO returns %d", res); + thread_printf ("WFSO returns %d, id %p", res, id); } + ResetEvent (*this); id = 0; + __name = NULL; + /* Mark the thread as available by setting avail to non-zero */ (void) InterlockedExchange ((LPLONG) &this->avail, avail); } } diff --git a/winsup/cygwin/cygthread.h b/winsup/cygwin/cygthread.h index b4f6cbe2fbd..7f5a594556f 100644 --- a/winsup/cygwin/cygthread.h +++ b/winsup/cygwin/cygthread.h @@ -29,3 +29,5 @@ class cygthread void * operator new (size_t); void exit_thread (); }; + +#define cygself NULL diff --git a/winsup/cygwin/debug.cc b/winsup/cygwin/debug.cc index 280cbb44d16..418f35510b8 100644 --- a/winsup/cygwin/debug.cc +++ b/winsup/cygwin/debug.cc @@ -101,17 +101,9 @@ newh () for (hl = cygheap->debug.freeh; hl < cygheap->debug.freeh + NFREEH; hl++) if (hl->name == NULL) - goto out; - - /* All used up??? */ - if ((hl = (handle_list *) malloc (sizeof *hl)) != NULL) - { - memset (hl, 0, sizeof (*hl)); - hl->allocated = TRUE; - } + return hl; -out: - return hl; + return NULL; } /* Add a handle to the linked list of known handles. */ @@ -136,8 +128,8 @@ add_handle (const char *func, int ln, HANDLE h, const char *name, bool inh) if ((hl = newh ()) == NULL) { here.unlock (); - system_printf ("couldn't allocate memory for %s(%d): %s(%p)", - func, ln, name, h); + debug_printf ("couldn't allocate memory for %s(%d): %s(%p)", + func, ln, name, h); return; } hl->h = h; @@ -160,10 +152,7 @@ delete_handle (handle_list *hl) handle_list *hnuke = hl->next; debug_printf ("nuking handle '%s'", hnuke->name); hl->next = hl->next->next; - if (hnuke->allocated) - free (hnuke); - else - memset (hnuke, 0, sizeof (*hnuke)); + memset (hnuke, 0, sizeof (*hnuke)); } void diff --git a/winsup/cygwin/debug.h b/winsup/cygwin/debug.h index 90b202543c8..09a74e9170a 100644 --- a/winsup/cygwin/debug.h +++ b/winsup/cygwin/debug.h @@ -23,7 +23,7 @@ DWORD __stdcall WFMO (DWORD, CONST HANDLE *, BOOL, DWORD) __attribute__ ((regpar } #define WaitForSingleObject WFSO -#define WaitForMultipleObject WFMO +#define WaitForMultipleObjects WFMO #if !defined(_DEBUG_H_) #define _DEBUG_H_ @@ -81,7 +81,6 @@ extern int pinger; struct handle_list { - BOOL allocated; HANDLE h; const char *name; const char *func; diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc index 9a32709fcd4..b073c940cad 100644 --- a/winsup/cygwin/fhandler_tty.cc +++ b/winsup/cygwin/fhandler_tty.cc @@ -68,7 +68,7 @@ fhandler_tty_master::init (int ntty) h = new cygthread (process_ioctl, NULL, "ttyioctl"); SetThreadPriority (*h, THREAD_PRIORITY_HIGHEST); - output_thread = new cygthread (process_output, NULL, "ttyout"); + output_thread = new cygthread (process_output, cygself, "ttyout"); SetThreadPriority (*output_thread, THREAD_PRIORITY_HIGHEST); return 0; @@ -368,7 +368,7 @@ out: } static DWORD WINAPI -process_output (void *) +process_output (void *self) { char buf[OUT_BUFFER_SIZE*2]; @@ -379,7 +379,7 @@ process_output (void *) { if (n < 0) termios_printf ("ReadFile %E"); - cygthread *t = tty_master->output_thread; + cygthread *t = (cygthread *) self; tty_master->output_thread = NULL; t->exit_thread (); } diff --git a/winsup/cygwin/grp.cc b/winsup/cygwin/grp.cc index c26a6b1c752..5bfb50e1471 100644 --- a/winsup/cygwin/grp.cc +++ b/winsup/cygwin/grp.cc @@ -365,7 +365,8 @@ getgroups32 (int gidsetsize, __gid32_t *grouplist, __gid32_t gid, for (int gidx = 0; (gr = internal_getgrent (gidx)); ++gidx) if (sid.getfromgr (gr)) for (DWORD pg = 0; pg < groups->GroupCount; ++pg) - if (sid == groups->Groups[pg].Sid) + if (sid == groups->Groups[pg].Sid && + sid != well_known_world_sid) { if (cnt < gidsetsize) grouplist[cnt] = gr->gr_gid; @@ -516,5 +517,4 @@ setgroups (int ngroups, const __gid16_t *grouplist) grouplist32[i] = grouplist[i]; } return setgroups32 (ngroups, grouplist32); - } diff --git a/winsup/cygwin/how-signals-work.txt b/winsup/cygwin/how-signals-work.txt new file mode 100644 index 00000000000..3b36badc826 --- /dev/null +++ b/winsup/cygwin/how-signals-work.txt @@ -0,0 +1,132 @@ +Copyright 2001 Red Hat Inc., Christopher Faylor + +How do signals work? + +On process startup, cygwin starts a secondary thread that deals with signals. +This thread contains a loop which blocks waiting for one of three events: + +1) sigcatch_main - a semaphore which, when incremented, indicates that a + signal may be available for the main thread. The caller waits for the + signal to be delivered before returning. + +2) sigcatch_nonmain - a semaphore which , when incremented, indicates that + a signal is available for a non-main thread (currently this is not truly + implemented). The caller waits for the signal to be delivered before + returning. + +3) sigcatch_nosync - a semaphore which, when incremented, indicates that + a signal may be available for the main thread. The caller does not wait + for the delivery of the signal before returning. + +So, the signal handler blocks waiting for one of these three semaphores. + +If one of these is activated, then the the signal handler inspects an +array of integers looking for a non-zero value. The array corresponds +to the normal UNIX signals + two extra locations for internal usage. +This array is located in the 'sigtodo' array in the procinfo class. + +The signal thread uses the InterlockedDecrement function to atomically +inspect elements of the array. If one one of the elements of the array +is non-zero, then cygwin checks to see if the user has blocked the +signal by inspecting the process signal mask. If the signal is blocked, +then the current array element is reincremented and the next element is +checked. + +If the signal is not blocked, then the function "sig_handle" is called +with the signal number as an argument. This is a fairly straightforward +function. It first checks to see if the signal is special in any way. + +A special signal is something like SIGKILL or SIGSTOP. The user has no +control over how those signals affect a UNIX process. If a SIGKILL is +received then sig_handle calls exit_sig to exit the process. If SIGSTOP +is called then sig_handle calls the regular signal dispatch function +with a special function argument "sig_handle_tty_stop". The signal +dispatch function is described below. + +An uncaught signal like SIGTERM or SIGHUP will cause the process to exit +with the standard UNIX exit values. Uncaught signals like SIGUSR1 are +ignored, as on UNIX. + +If the signal has an associated signal handler, then the setup_handler +function is eventually called. It is passed the signal, the address of +the handler, and a standard UNIX sigaction structure. The meat of +signal processing is in setup_handler. + +setup_handler has a "simple" task. It tries to stop the appropriate +thread and redirect its execution to the signal handler function. +Currently, the "appropriate thread" is only the main thread. Someday +we'll have to change this to allow cygwin to interrupt other user +threads. + +To accomplish its task, setup_handler first inspects the static sigsave +structure. This structure contains information on any not-yet-handled +signals that may have been set up by a previous call to setup_handler +but not yet dispatched in the main thread. If the sigsave structure +seems to be "active", then a "pending" flag is set (see below) and the +function returns. Otherwise processing continues. + +After determining that sigsave is available, setup_handler will take one +of two routes, depending on whether the main thread is executing in the +cygwin DLL or is currently in "user" code. We'll discuss the cygwin DLL +case first. + +If sigsave seems to be available, then the frame information for the +main thread is inspected. This information is set by any cygwin +function that is known to block (such as _read()), usually by calling +'sigframe thisframe (mainthread)' in the cygwin function. This call +sets up information about the current stack frame of an executing cygwin +process. Any function which uses 'sigframe thisframe' should be signal +aware. It should detect when a signal has arrived and return +immediately. This method is also used throughout the DLL to ensure +accurate frame info for the executing function. So, you'll see it +sprinkled liberally throughout the DLL, usually at places where +empirical tests have indicated problems finding this address via the +brute force method stack walking method employed in setup_handler. + +So, if mainframe is active, that means that we have good information +about the state of the main thread. Cygwin uses the stack frame info +from this structure to insert a call to the assembly language function +'sigdelayed' in place of the main thread's normal return address. So, +when a call to (e.g.) _read returns after detecting a signal, it does +not return to its caller. Rather, it returns to sigdelayed. + +The sigdelayed function saves a lot of state on the stack and sets the +signal mask as appropriate for POSIX. It uses information from the +sigsave structure which has been filled in by interrupt_on_return, as +called by setup_handler. sigdelayed pushes a "call" to the function +"sigreturn" on the stack. This will be the return address seen by the +signal handler. After setting up the return value, modifying the signal +mask, and saving other information on the stack, sigreturn clears the +sigsave structure (so that setup_handler can use it) and jumps to the +signal handler function. And, so a UNIX signal handler function is +emulated. + +The signal handler function operates as normal for UNIX but, upon +return, it does not go directly back to the return address of the +original cygwin function. Instead it returns to the previously +mentioned 'sigreturn' assembly language function. + +sigreturn resets the process mask to its state prior to calling the +signal handler. It checks to see if any new signals have come in and +calls the handler for them now, ensuring that the order of signal +arrival is more or less maintained. It checks to see if a cygwin +routine has set a special "restore this errno on returning from a +signal" value and sets errno to this, if so. Finally, it restores all +of the register values that were in effect when sigdelayed was called. + +Ok, you thought I had forgotten about the 'pending' stuff didn't you? +Well, if you can rewind up to the discussion of sig_handle, we'll return +to the situation where sigsave was currently active. In this case, +setup_handler will set a "pending" flag, will reincrement the appropriate +element of the above signal array, and will return 0 to indicate that +the interrupt did not occur. Otherwise setup_handler returns 1. + +For pending signals, the theory is that the signal handler thread will +be forced to be rerun by having some strategic cygwin function call +sig_send with a __SIGFLUSH "argument" to it. This causes the signal +handler to rescan the signal array looking for pending signals. + +This leads us to the sig_send function. This is the "client side" part +of the signal manipulation process. sig_send is the low-level function +called by a high level process like kill(). You would use sig_send +to send a __SIGFLUSH to the signal thread. diff --git a/winsup/cygwin/security.cc b/winsup/cygwin/security.cc index d12e3335954..f22c7dc8f0a 100644 --- a/winsup/cygwin/security.cc +++ b/winsup/cygwin/security.cc @@ -779,13 +779,16 @@ verify_token (HANDLE token, cygsid &usersid, user_groups &groups, BOOL *pintern) saw[pos] = TRUE; else if (groups.pgsid == gsid) sawpg = TRUE; - else + else if (gsid != well_known_world_sid && + gsid != usersid) goto done; } for (int gidx = 0; gidx < groups.sgsids.count; gidx++) if (!saw[gidx]) goto done; - if (sawpg || groups.sgsids.contains (groups.pgsid)) + if (sawpg || + groups.sgsids.contains (groups.pgsid) || + groups.pgsid == usersid) ret = TRUE; } done: diff --git a/winsup/cygwin/sigproc.cc b/winsup/cygwin/sigproc.cc index 49947840ee0..4914e6a24e3 100644 --- a/winsup/cygwin/sigproc.cc +++ b/winsup/cygwin/sigproc.cc @@ -571,7 +571,7 @@ sigproc_init () signal_arrived = CreateEvent(&sec_none_nih, TRUE, FALSE, NULL); ProtectHandle (signal_arrived); - hwait_sig = new cygthread (wait_sig, NULL, "sig"); + hwait_sig = new cygthread (wait_sig, cygself, "sig"); /* sync_proc_subproc is used by proc_subproc. It serialises * access to the children and zombie arrays. @@ -828,6 +828,7 @@ init_child_info (DWORD chtype, child_info *ch, pid_t pid, HANDLE subproc_ready) ch->subproc_ready = subproc_ready; ch->pppid_handle = myself->ppid_handle; ch->fhandler_union_cb = sizeof (fhandler_union); + ch->mount_h = cygwin_mount_h; } /* Check the state of all of our children to see if any are stopped or @@ -1030,10 +1031,10 @@ stopped_or_terminated (waitq *parent_w, _pinfo *child) * has been handled, as per POSIX. */ static DWORD WINAPI -wait_sig (VOID *) +wait_sig (VOID *self) { /* Initialization */ - (void) SetThreadPriority (*hwait_sig, WAIT_SIG_PRIORITY); + (void) SetThreadPriority (*((cygthread *) self), WAIT_SIG_PRIORITY); /* sigcatch_nosync - semaphore incremented by sig_dispatch_pending and * by foreign processes to force an examination of diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc index 7454adc58b4..602c0af6919 100644 --- a/winsup/cygwin/spawn.cc +++ b/winsup/cygwin/spawn.cc @@ -608,7 +608,6 @@ spawn_guts (const char * prog_arg, const char *const *argv, char sa_buf[1024]; cygbench ("spawn-guts"); - ciresrv.mount_h = cygwin_mount_h; if (!cygheap->user.issetuid ()) { diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc index 4792744e026..31eed42b2e8 100644 --- a/winsup/cygwin/syscalls.cc +++ b/winsup/cygwin/syscalls.cc @@ -2004,7 +2004,7 @@ seteuid32 (__uid32_t uid) else { CloseHandle (ptok); - return 0; /* No change */ + goto success; /* No change */ } } @@ -2025,7 +2025,7 @@ seteuid32 (__uid32_t uid) CloseHandle (ptok); if (!ImpersonateLoggedOnUser (cygheap->user.token)) system_printf ("Impersonating in seteuid failed: %E"); - return 0; /* No change */ + goto success; /* No change */ } } } @@ -2097,6 +2097,7 @@ seteuid32 (__uid32_t uid) CloseHandle (sav_token); cygheap->user.set_name (pw_new->pw_name); cygheap->user.set_sid (usersid); +success: myself->uid = uid; groups.ischanged = FALSE; return 0; |