protect sem_open against cancellation

also fix one minor bug: failure to free the early-reserved slot when
the semaphore later found to already be mapped.
This commit is contained in:
Rich Felker
2012-09-30 19:44:45 -04:00
parent bf258341b7
commit e44849f5cf

View File

@ -29,7 +29,7 @@ sem_t *sem_open(const char *name, int flags, ...)
va_list ap; va_list ap;
mode_t mode; mode_t mode;
unsigned value; unsigned value;
int fd, i, e, slot, first=1, cnt; int fd, i, e, slot, first=1, cnt, cs;
sem_t newsem; sem_t newsem;
void *map; void *map;
char tmp[64]; char tmp[64];
@ -74,6 +74,8 @@ sem_t *sem_open(const char *name, int flags, ...)
return SEM_FAILED; return SEM_FAILED;
} }
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
for (;;) { for (;;) {
/* If exclusive mode is not requested, try opening an /* If exclusive mode is not requested, try opening an
* existing file first and fall back to creation. */ * existing file first and fall back to creation. */
@ -83,16 +85,16 @@ sem_t *sem_open(const char *name, int flags, ...)
if ((map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED || if ((map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED ||
fstat(fd, &st) < 0) { fstat(fd, &st) < 0) {
close(fd); close(fd);
return SEM_FAILED; goto fail;
} }
close(fd); close(fd);
break; break;
} }
if (errno != ENOENT) if (errno != ENOENT)
return SEM_FAILED; goto fail;
} }
if (!(flags & O_CREAT)) if (!(flags & O_CREAT))
return SEM_FAILED; goto fail;
if (first) { if (first) {
first = 0; first = 0;
va_start(ap, flags); va_start(ap, flags);
@ -101,7 +103,7 @@ sem_t *sem_open(const char *name, int flags, ...)
va_end(ap); va_end(ap);
if (value > SEM_VALUE_MAX) { if (value > SEM_VALUE_MAX) {
errno = EINVAL; errno = EINVAL;
return SEM_FAILED; goto fail;
} }
sem_init(&newsem, 1, value); sem_init(&newsem, 1, value);
} }
@ -112,13 +114,13 @@ sem_t *sem_open(const char *name, int flags, ...)
fd = open(tmp, O_CREAT|O_EXCL|FLAGS, mode); fd = open(tmp, O_CREAT|O_EXCL|FLAGS, mode);
if (fd < 0) { if (fd < 0) {
if (errno == EEXIST) continue; if (errno == EEXIST) continue;
return SEM_FAILED; goto fail;
} }
if (write(fd, &newsem, sizeof newsem) != sizeof newsem || fstat(fd, &st) < 0 || if (write(fd, &newsem, sizeof newsem) != sizeof newsem || fstat(fd, &st) < 0 ||
(map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) { (map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
close(fd); close(fd);
unlink(tmp); unlink(tmp);
return SEM_FAILED; goto fail;
} }
close(fd); close(fd);
if (link(tmp, name) == 0) break; if (link(tmp, name) == 0) break;
@ -128,7 +130,7 @@ sem_t *sem_open(const char *name, int flags, ...)
* otherwise, next iteration will try to open the * otherwise, next iteration will try to open the
* existing file. */ * existing file. */
if (e != EEXIST || flags == (O_CREAT|O_EXCL)) if (e != EEXIST || flags == (O_CREAT|O_EXCL))
return SEM_FAILED; goto fail;
} }
/* See if the newly mapped semaphore is already mapped. If /* See if the newly mapped semaphore is already mapped. If
@ -138,16 +140,20 @@ sem_t *sem_open(const char *name, int flags, ...)
for (i=0; i<SEM_NSEMS_MAX && semtab[i].ino != st.st_ino; i++); for (i=0; i<SEM_NSEMS_MAX && semtab[i].ino != st.st_ino; i++);
if (i<SEM_NSEMS_MAX) { if (i<SEM_NSEMS_MAX) {
munmap(map, sizeof(sem_t)); munmap(map, sizeof(sem_t));
semtab[i].refcnt++; semtab[slot].sem = 0;
UNLOCK(lock); slot = i;
return semtab[i].sem; map = semtab[i].sem;
} }
semtab[slot].refcnt = 1; semtab[slot].refcnt++;
semtab[slot].sem = map; semtab[slot].sem = map;
semtab[slot].ino = st.st_ino; semtab[slot].ino = st.st_ino;
UNLOCK(lock); UNLOCK(lock);
pthread_setcancelstate(cs, 0);
return map; return map;
fail:
pthread_setcancelstate(cs, 0);
return SEM_FAILED;
} }
int sem_close(sem_t *sem) int sem_close(sem_t *sem)