sem: Don't free nameinfo that is still in list when open() fails.

This bug could be reproduced easily by calling sem_open() with O_CREAT |
O_EXCL on a semaphore that is already open in the process. The struct
sem_nameinfo would be freed while still in sem_list and later calls to
sem_open() or sem_close() could access freed memory.

PR:		206396
MFC after:	5 days
This commit is contained in:
Jilles Tjoelker 2016-01-22 14:52:31 +00:00
parent 6362080245
commit afa04e4170
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=294565
2 changed files with 38 additions and 1 deletions

View File

@ -177,8 +177,10 @@ _sem_open(const char *name, int flags, ...)
if (ni->name != NULL && strcmp(name, ni->name) == 0) {
fd = _open(path, flags | O_RDWR | O_CLOEXEC |
O_EXLOCK, mode);
if (fd == -1 || _fstat(fd, &sb) == -1)
if (fd == -1 || _fstat(fd, &sb) == -1) {
ni = NULL;
goto error;
}
if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT |
O_EXCL) || ni->dev != sb.st_dev ||
ni->ino != sb.st_ino) {

View File

@ -7,6 +7,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
@ -14,6 +15,7 @@
int test_unnamed(void);
int test_named(void);
int test_named2(void);
int
test_unnamed(void)
@ -93,10 +95,43 @@ test_named(void)
return (0);
}
int
test_named2(void)
{
sem_t *s, *s2, *s3;
printf("testing named process-shared semaphore, O_EXCL cases\n");
sem_unlink(SEM_NAME);
s = sem_open(SEM_NAME, O_CREAT | O_EXCL, 0777, 0);
if (s == SEM_FAILED)
err(1, "sem_open failed");
s2 = sem_open(SEM_NAME, O_CREAT | O_EXCL, 0777, 0);
if (s2 != SEM_FAILED)
errx(2, "second sem_open call wrongly succeeded");
if (errno != EEXIST)
err(3, "second sem_open call failed with wrong errno");
s3 = sem_open(SEM_NAME, 0);
if (s3 == SEM_FAILED)
err(4, "third sem_open call failed");
if (s != s3)
errx(5,
"two sem_open calls for same semaphore do not return same address");
if (sem_close(s3))
err(6, "sem_close failed");
if (sem_close(s))
err(7, "sem_close failed");
printf("OK.\n");
return (0);
}
int
main(void)
{
test_unnamed();
test_named();
test_named2();
return (0);
}