Bugzilla – Attachment 1347 Details for
Bug 688
PAM modules relying on module-private data (pam_dhkeys, pam_krb5, AFS) fail
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
uset setjmp/longjmp instead of pthreads or fork
p (text/plain), 6.52 KB, created by
David Leonard
on 2007-09-13 13:46:53 AEST
(
hide
)
Description:
uset setjmp/longjmp instead of pthreads or fork
Filename:
MIME Type:
Creator:
David Leonard
Created:
2007-09-13 13:46:53 AEST
Size:
6.52 KB
patch
obsolete
>Index: openssh/auth-pam.c >=================================================================== >--- openssh/auth-pam.c (revision 194) >+++ openssh/auth-pam.c (working copy) >@@ -114,6 +114,10 @@ > * this breaks. > */ > #ifdef UNSUPPORTED_POSIX_THREADS_HACK >+#if defined(USE_UTASK_HACK) >+struct task; >+typedef struct task *sp_pthread_t; >+#else /* !USE_UTASK_HACK */ > #include <pthread.h> > /* > * Avoid namespace clash when *not* using pthreads for systems *with* >@@ -121,6 +125,7 @@ > * (e.g. Linux) > */ > typedef pthread_t sp_pthread_t; >+#endif /* !USE_UTASK_HACK */ > #else > typedef pid_t sp_pthread_t; > #endif >@@ -135,8 +140,217 @@ > static void sshpam_free_ctx(void *); > static struct pam_ctxt *cleanup_ctxt; > >-#ifndef UNSUPPORTED_POSIX_THREADS_HACK >+#if defined(UNSUPPORTED_POSIX_THREADS_HACK) && defined(USE_UTASK_HACK) > /* >+ * Simulate threads using setjmp/longjmp and sigaltstack. >+ * This works for exactly two cooperative tasks, which is all that >+ * PAM authentication needs. >+ */ >+ >+#include <setjmp.h> >+#ifdef HAVE_POLL_H >+#include <poll.h> >+#endif >+ >+#define utask_jmp_buf sigjmp_buf >+#define utask_setjmp(b) sigsetjmp(b, 0) >+#define utask_longjmp(b,x) siglongjmp(b, x) >+ >+/* A task structure; mainly a container for the jmpbuf to save task state */ >+struct task { >+ const char *name; >+ enum { UTASK_DEAD, UTASK_RUNNABLE, UTASK_EXITED } state; >+ utask_jmp_buf jmpbuf; >+ void *(*entry)(void *); >+ void *entry_arg; >+ void *return_val; >+ char *stack; >+ struct task *next, **nextp; >+}; >+static struct task main_task = { "main", UTASK_RUNNABLE }; >+static struct task other_task = { "other", UTASK_DEAD }; >+static struct task * volatile current_task = &main_task; >+static struct task *sleeping_tasks; >+ >+/* Switches to a task. */ >+static void >+utask_switch(struct task *new_task) >+{ >+ struct task *old_task; >+ >+ if (current_task != new_task) { >+ old_task = current_task; >+ current_task = new_task; >+ if (utask_setjmp(old_task->jmpbuf) == 0) >+ utask_longjmp(current_task->jmpbuf, 1); >+ } >+} >+ >+/* Yields to next runnable task. (cancellation point) */ >+static void >+pthread_yield() >+{ >+ struct task *next_task; >+ >+ /* Determine which task to yield to. Easy since there are only 2 tasks */ >+ next_task = current_task->next; >+ next_task = (current_task == &main_task ? &other_task : &main_task); >+ >+ if (next_task->state == UTASK_RUNNABLE) >+ utask_switch(next_task); >+} >+ >+/* Terminates the current task. */ >+static void >+pthread_exit(void *arg) >+{ >+ if (current_task == &main_task) >+ fatal("pthread_exit: main task exited"); >+ current_task->return_val = arg; >+ current_task->state = UTASK_EXITED; >+ pthread_yield(); >+} >+ >+/* Signal handler called for the USR1 signal raised from pthread_create() */ >+static void >+sig_usr1(int ign) >+{ >+ /* We're on a completely different stack now, so switch task pointers >+ * to run the new task. */ >+ current_task->state = UTASK_RUNNABLE; >+ pthread_yield(); >+ pthread_exit((*other_task.entry)(other_task.entry_arg)); >+} >+ >+/* Creates a second task/thread that runs on a separate stack. */ >+static int >+pthread_create(struct task **t, void *attr, void *(*entry)(void *), void *arg) >+{ >+ stack_t sigstk, oldsigstk; >+ struct sigaction sigact, oldsigact; >+ struct task *new_task; >+ int stacksz, stackpad; >+ >+ if (current_task != &main_task) >+ fatal("pthread_create: not called from main task"); >+ if (other_task.state == UTASK_RUNNABLE) >+ fatal("pthread_create: other task still active"); >+ >+ /* Choosing a new task structure is easy. */ >+ new_task = &other_task; >+ >+ new_task->entry = entry; >+ new_task->entry_arg = arg; >+ >+ /* 4MB of stack with 8kB of padding either side */ >+ stacksz = 4 * 1024 * 1024; >+ stackpad = 8 * 1024; >+ if (!new_task->stack) >+ new_task->stack = xmalloc(stacksz + stackpad * 2); /* 4MB */ >+ >+ /* Set up an alternate signal stack, which is the stack that >+ * sig_usr1() will run on. We use this to separate the tasks stacks, >+ * in a kind-of-portable way. */ >+ memset(&sigstk, 0, sizeof sigstk); >+ sigstk.ss_sp = new_task->stack + stackpad; >+ sigstk.ss_size = stacksz; >+ sigstk.ss_flags = 0; >+ if (sigaltstack(&sigstk, &oldsigstk) < 0) >+ err(1, "sigaltstack"); >+ >+ /* Set up SIGUSR1 to call sig_usr1() */ >+ memset(&sigact, 0, sizeof sigact); >+ sigact.sa_handler = sig_usr1; >+ sigact.sa_flags = /* SA_RESETHAND | */ SA_ONSTACK; >+ if (sigaction(SIGUSR1, &sigact, &oldsigact) < 0) >+ err(1, "sigaction"); >+ >+ if (utask_setjmp(current_task->jmpbuf) == 0) { >+ /* Trigger sig_usr1() */ >+ current_task = new_task; >+ if (kill(getpid(), SIGUSR1) == -1) >+ err(1, "kill"); >+ while (new_task->state != UTASK_RUNNABLE) >+ pause(); >+ /* The signal handler returned (usually from pthread_exit()). */ >+ current_task = &main_task; >+ utask_longjmp(current_task->jmpbuf, 1); >+ } >+ /* sig_usr1() will longjmp back to here via pthread_yield() */ >+ >+ /* Restore the signal stack */ >+ if (sigaltstack(&oldsigstk, (stack_t *)0) < 0) >+ err(1, "sigaltstack"); >+ /* Restore the signal handler to its old behaviour */ >+ if (sigaction(SIGUSR1, &oldsigact, (struct sigaction *)0) < 0) >+ err(1, "sigaction"); >+ >+ /* Yield back to the newly started task */ >+ pthread_yield(); >+ >+ if (t) >+ *t = new_task; >+ return 0; >+} >+ >+/* Waits for a task to exit. (cancellation point) */ >+static int >+pthread_join(struct task *t, void **ret) >+{ >+ pthread_yield(); >+ if (current_task != &main_task) >+ errx(1, "pthread_join: only callable from the main task"); >+ while (t->state == UTASK_RUNNABLE) >+ pthread_yield(); >+ if (t->stack) { >+ free(t->stack); >+ t->stack = NULL; >+ } >+ if (ret) >+ *ret = t->return_val; >+ return 0; >+} >+ >+/* Cancels a task. */ >+static int >+pthread_cancel(struct task *t) >+{ >+ /* Because these tasks are cooperative, they must be at >+ * a cancellation point */ >+ if (t->state == UTASK_RUNNABLE) { >+ t->state = UTASK_DEAD; >+ t->return_val = 0; >+ } >+ return 0; >+} >+ >+/* Yields until a file descriptor becomes ready for read (cancellation point) */ >+static int >+utask_yield_fd(int fd) >+{ >+ struct pollfd pfd[1]; >+ int n; >+ >+ for (;;) { >+ pfd[0].fd = fd; >+ pfd[0].events = POLLIN; >+ pfd[0].revents = 0; >+ n = poll(pfd, 1, 0); >+ if (n < 0) { >+ if (errno == EAGAIN) >+ continue; >+ err(1, "utask_yield_fd: poll"); >+ } else if (n > 0) >+ return 1; >+ pthread_yield(); >+ } >+} >+ >+/* The only function below that needs to yield is ssh_msg_recv */ >+#define ssh_msg_recv(s,b) (utask_yield_fd(s), ssh_msg_recv(s,b)) >+ >+#else >+/* > * Simulate threads with processes. > */ >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 688
:
434
|
642
|
643
| 1347