View | Details | Raw Unified | Return to bug 688 | Differences between
and this patch

Collapse All | Expand All

(-)openssh/auth-pam.c (-1 / +215 lines)
Lines 114-119 Link Here
114
 * this breaks.
114
 * this breaks.
115
 */
115
 */
116
#ifdef UNSUPPORTED_POSIX_THREADS_HACK
116
#ifdef UNSUPPORTED_POSIX_THREADS_HACK
117
#if defined(USE_UTASK_HACK)
118
struct task;
119
typedef struct task *sp_pthread_t;
120
#else /* !USE_UTASK_HACK */
117
#include <pthread.h>
121
#include <pthread.h>
118
/*
122
/*
119
 * Avoid namespace clash when *not* using pthreads for systems *with*
123
 * Avoid namespace clash when *not* using pthreads for systems *with*
Lines 121-126 Link Here
121
 * (e.g. Linux)
125
 * (e.g. Linux)
122
 */
126
 */
123
typedef pthread_t sp_pthread_t;
127
typedef pthread_t sp_pthread_t;
128
#endif /* !USE_UTASK_HACK */
124
#else
129
#else
125
typedef pid_t sp_pthread_t;
130
typedef pid_t sp_pthread_t;
126
#endif
131
#endif
Lines 135-142 Link Here
135
static void sshpam_free_ctx(void *);
140
static void sshpam_free_ctx(void *);
136
static struct pam_ctxt *cleanup_ctxt;
141
static struct pam_ctxt *cleanup_ctxt;
137
142
138
#ifndef UNSUPPORTED_POSIX_THREADS_HACK
143
#if defined(UNSUPPORTED_POSIX_THREADS_HACK) && defined(USE_UTASK_HACK)
139
/*
144
/*
145
 * Simulate threads using setjmp/longjmp and sigaltstack.
146
 * This works for exactly two cooperative tasks, which is all that
147
 * PAM authentication needs.
148
 */
149
150
#include <setjmp.h>
151
#ifdef HAVE_POLL_H
152
#include <poll.h>
153
#endif
154
155
#define utask_jmp_buf	    sigjmp_buf
156
#define utask_setjmp(b)	    sigsetjmp(b, 0)
157
#define utask_longjmp(b,x)  siglongjmp(b, x)
158
159
/* A task structure; mainly a container for the jmpbuf to save task state */
160
struct task {
161
    const char *name;
162
    enum { UTASK_DEAD, UTASK_RUNNABLE, UTASK_EXITED } state;
163
    utask_jmp_buf jmpbuf;
164
    void *(*entry)(void *);
165
    void *entry_arg;
166
    void *return_val;
167
    char *stack;
168
    struct task *next, **nextp;
169
};
170
static struct task main_task  = { "main", UTASK_RUNNABLE };
171
static struct task other_task = { "other", UTASK_DEAD };
172
static struct task * volatile current_task = &main_task;
173
static struct task *sleeping_tasks;
174
175
/* Switches to a task. */
176
static void
177
utask_switch(struct task *new_task)
178
{
179
    struct task *old_task;
180
181
    if (current_task != new_task) {
182
       	old_task = current_task;
183
	current_task = new_task;
184
	if (utask_setjmp(old_task->jmpbuf) == 0)
185
	    utask_longjmp(current_task->jmpbuf, 1);
186
    }
187
}
188
189
/* Yields to next runnable task. (cancellation point) */
190
static void
191
pthread_yield()
192
{
193
    struct task *next_task;
194
195
    /* Determine which task to yield to. Easy since there are only 2 tasks */
196
    next_task = current_task->next;
197
    next_task = (current_task == &main_task ? &other_task : &main_task);
198
199
    if (next_task->state == UTASK_RUNNABLE)
200
	utask_switch(next_task);
201
}
202
203
/* Terminates the current task. */
204
static void
205
pthread_exit(void *arg)
206
{
207
    if (current_task == &main_task)
208
       	fatal("pthread_exit: main task exited");
209
    current_task->return_val = arg;
210
    current_task->state = UTASK_EXITED;
211
    pthread_yield();
212
}
213
214
/* Signal handler called for the USR1 signal raised from pthread_create() */
215
static void
216
sig_usr1(int ign)
217
{
218
    /* We're on a completely different stack now, so switch task pointers
219
     * to run the new task. */
220
    current_task->state = UTASK_RUNNABLE;
221
    pthread_yield();
222
    pthread_exit((*other_task.entry)(other_task.entry_arg));
223
}
224
225
/* Creates a second task/thread that runs on a separate stack. */
226
static int
227
pthread_create(struct task **t, void *attr, void *(*entry)(void *), void *arg)
228
{
229
    stack_t sigstk, oldsigstk;
230
    struct sigaction sigact, oldsigact;
231
    struct task *new_task;
232
    int stacksz, stackpad;
233
234
    if (current_task != &main_task)
235
	fatal("pthread_create: not called from main task");
236
    if (other_task.state == UTASK_RUNNABLE)
237
	fatal("pthread_create: other task still active");
238
239
    /* Choosing a new task structure is easy. */
240
    new_task = &other_task;
241
   
242
    new_task->entry = entry;
243
    new_task->entry_arg = arg;
244
245
    /* 4MB of stack with 8kB of padding either side */
246
    stacksz = 4 * 1024 * 1024;
247
    stackpad = 8 * 1024;
248
    if (!new_task->stack)
249
	new_task->stack = xmalloc(stacksz + stackpad * 2); /* 4MB */
250
251
    /* Set up an alternate signal stack, which is the stack that
252
     * sig_usr1() will run on. We use this to separate the tasks stacks, 
253
     * in a kind-of-portable way. */
254
    memset(&sigstk, 0, sizeof sigstk);
255
    sigstk.ss_sp = new_task->stack + stackpad;
256
    sigstk.ss_size = stacksz;
257
    sigstk.ss_flags = 0;
258
    if (sigaltstack(&sigstk, &oldsigstk) < 0)
259
	err(1, "sigaltstack");
260
261
    /* Set up SIGUSR1 to call sig_usr1() */
262
    memset(&sigact, 0, sizeof sigact);
263
    sigact.sa_handler = sig_usr1;
264
    sigact.sa_flags = /* SA_RESETHAND | */ SA_ONSTACK;
265
    if (sigaction(SIGUSR1, &sigact, &oldsigact) < 0)
266
	err(1, "sigaction");
267
268
    if (utask_setjmp(current_task->jmpbuf) == 0) {
269
	/* Trigger sig_usr1() */
270
	current_task = new_task;
271
	if (kill(getpid(), SIGUSR1) == -1)
272
	    err(1, "kill");
273
	while (new_task->state != UTASK_RUNNABLE)
274
	    pause();
275
	/* The signal handler returned (usually from pthread_exit()). */
276
	current_task = &main_task;
277
	utask_longjmp(current_task->jmpbuf, 1);
278
    }
279
    /* sig_usr1() will longjmp back to here via pthread_yield() */
280
281
    /* Restore the signal stack */
282
    if (sigaltstack(&oldsigstk, (stack_t *)0) < 0)
283
	err(1, "sigaltstack");
284
    /* Restore the signal handler to its old behaviour */
285
    if (sigaction(SIGUSR1, &oldsigact, (struct sigaction *)0) < 0)
286
	err(1, "sigaction");
287
288
    /* Yield back to the newly started task */
289
    pthread_yield();
290
291
    if (t)
292
	*t = new_task;
293
    return 0;
294
}
295
296
/* Waits for a task to exit. (cancellation point) */
297
static int
298
pthread_join(struct task *t, void **ret)
299
{
300
    pthread_yield();
301
    if (current_task != &main_task) 
302
	errx(1, "pthread_join: only callable from the main task");
303
    while (t->state == UTASK_RUNNABLE)
304
	pthread_yield();
305
    if (t->stack) {
306
	free(t->stack);
307
	t->stack = NULL;
308
    }
309
    if (ret)
310
	*ret = t->return_val;
311
    return 0;
312
}
313
314
/* Cancels a task. */
315
static int
316
pthread_cancel(struct task *t)
317
{
318
    /* Because these tasks are cooperative, they must be at 
319
     * a cancellation point */
320
    if (t->state == UTASK_RUNNABLE) {
321
	t->state = UTASK_DEAD;
322
	t->return_val = 0;
323
    }
324
    return 0;
325
}
326
327
/* Yields until a file descriptor becomes ready for read (cancellation point) */
328
static int
329
utask_yield_fd(int fd)
330
{
331
    struct pollfd pfd[1];
332
    int n;
333
334
    for (;;) {
335
	pfd[0].fd = fd;
336
	pfd[0].events = POLLIN;
337
	pfd[0].revents = 0;
338
	n = poll(pfd, 1, 0);
339
	if (n < 0) {
340
	    if (errno == EAGAIN)
341
		continue;
342
	    err(1, "utask_yield_fd: poll");
343
	} else if (n > 0)
344
	    return 1;
345
	pthread_yield();
346
    }
347
}
348
349
/* The only function below that needs to yield is ssh_msg_recv */
350
#define ssh_msg_recv(s,b) (utask_yield_fd(s), ssh_msg_recv(s,b))
351
352
#else
353
/*
140
 * Simulate threads with processes.
354
 * Simulate threads with processes.
141
 */
355
 */
142
356

Return to bug 688