Browse Source

Avouid a race in atexit() handling by introducing a lock. Problem

originally reported by Gergely Kovacs; help from dhartmei@;
ok tedu@ millert@
OPENBSD_3_9 OPENBSD_3_9_BASE
otto 19 years ago
parent
commit
790f0cc439
1 changed files with 20 additions and 9 deletions
  1. +20
    -9
      src/lib/libc/stdlib/atexit.c

+ 20
- 9
src/lib/libc/stdlib/atexit.c View File

@ -1,4 +1,4 @@
/* $OpenBSD: atexit.c,v 1.11 2005/10/26 18:55:26 otto Exp $ */
/* $OpenBSD: atexit.c,v 1.12 2006/02/22 07:16:32 otto Exp $ */
/* /*
* Copyright (c) 2002 Daniel Hartmeier * Copyright (c) 2002 Daniel Hartmeier
* All rights reserved. * All rights reserved.
@ -34,6 +34,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include "atexit.h" #include "atexit.h"
#include "thread_private.h"
int __atexit_invalid = 1; int __atexit_invalid = 1;
struct atexit *__atexit; struct atexit *__atexit;
@ -54,22 +55,25 @@ struct atexit *__atexit;
int int
atexit(void (*fn)(void)) atexit(void (*fn)(void))
{ {
struct atexit *p = __atexit;
struct atexit *p;
int pgsize = getpagesize(); int pgsize = getpagesize();
int ret = -1;
if (pgsize < sizeof(*p)) if (pgsize < sizeof(*p))
return (-1); return (-1);
_ATEXIT_LOCK();
p = __atexit;
if (p != NULL) { if (p != NULL) {
if (p->ind + 1 >= p->max) if (p->ind + 1 >= p->max)
p = NULL; p = NULL;
else if (mprotect(p, pgsize, PROT_READ | PROT_WRITE)) else if (mprotect(p, pgsize, PROT_READ | PROT_WRITE))
return (-1);
goto unlock;
} }
if (p == NULL) { if (p == NULL) {
p = mmap(NULL, pgsize, PROT_READ | PROT_WRITE, p = mmap(NULL, pgsize, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE, -1, 0); MAP_ANON | MAP_PRIVATE, -1, 0);
if (p == MAP_FAILED) if (p == MAP_FAILED)
return (-1);
goto unlock;
if (__atexit == NULL) { if (__atexit == NULL) {
p->fns[0] = NULL; p->fns[0] = NULL;
p->ind = 1; p->ind = 1;
@ -84,8 +88,11 @@ atexit(void (*fn)(void))
} }
p->fns[p->ind++] = fn; p->fns[p->ind++] = fn;
if (mprotect(p, pgsize, PROT_READ)) if (mprotect(p, pgsize, PROT_READ))
return (-1);
return (0);
goto unlock;
ret = 0;
unlock:
_ATEXIT_UNLOCK();
return (ret);
} }
/* /*
@ -94,18 +101,20 @@ atexit(void (*fn)(void))
void void
__atexit_register_cleanup(void (*fn)(void)) __atexit_register_cleanup(void (*fn)(void))
{ {
struct atexit *p = __atexit;
struct atexit *p;
int pgsize = getpagesize(); int pgsize = getpagesize();
if (pgsize < sizeof(*p)) if (pgsize < sizeof(*p))
return; return;
_ATEXIT_LOCK();
p = __atexit;
while (p != NULL && p->next != NULL) while (p != NULL && p->next != NULL)
p = p->next; p = p->next;
if (p == NULL) { if (p == NULL) {
p = mmap(NULL, pgsize, PROT_READ | PROT_WRITE, p = mmap(NULL, pgsize, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE, -1, 0); MAP_ANON | MAP_PRIVATE, -1, 0);
if (p == MAP_FAILED) if (p == MAP_FAILED)
return;
goto unlock;
p->ind = 1; p->ind = 1;
p->max = (pgsize - ((char *)&p->fns[0] - (char *)p)) / p->max = (pgsize - ((char *)&p->fns[0] - (char *)p)) /
sizeof(p->fns[0]); sizeof(p->fns[0]);
@ -115,8 +124,10 @@ __atexit_register_cleanup(void (*fn)(void))
__atexit_invalid = 0; __atexit_invalid = 0;
} else { } else {
if (mprotect(p, pgsize, PROT_READ | PROT_WRITE)) if (mprotect(p, pgsize, PROT_READ | PROT_WRITE))
return;
goto unlock;
} }
p->fns[0] = fn; p->fns[0] = fn;
mprotect(p, pgsize, PROT_READ); mprotect(p, pgsize, PROT_READ);
unlock:
_ATEXIT_UNLOCK();
} }

Loading…
Cancel
Save