Correct locking and cancellation cleanup in syslog functions (bug 26100)

Properly serialize the access to the global state shared between the
syslog functions, to avoid races in multithreaded processes.  Protect a
local allocation in the __vsyslog_internal function from leaking during
cancellation.
This commit is contained in:
Andreas Schwab 2020-06-23 12:55:49 +02:00
parent cb7e7a5ca1
commit c4e4b2e149
1 changed files with 28 additions and 16 deletions

View File

@ -91,14 +91,20 @@ struct cleanup_arg
static void static void
cancel_handler (void *ptr) cancel_handler (void *ptr)
{ {
#ifndef NO_SIGPIPE
/* Restore the old signal handler. */ /* Restore the old signal handler. */
struct cleanup_arg *clarg = (struct cleanup_arg *) ptr; struct cleanup_arg *clarg = (struct cleanup_arg *) ptr;
if (clarg != NULL && clarg->oldaction != NULL) if (clarg != NULL)
{
#ifndef NO_SIGPIPE
if (clarg->oldaction != NULL)
__sigaction (SIGPIPE, clarg->oldaction, NULL); __sigaction (SIGPIPE, clarg->oldaction, NULL);
#endif #endif
/* Free the memstream buffer, */
free (clarg->buf);
}
/* Free the lock. */ /* Free the lock. */
__libc_lock_unlock (syslog_lock); __libc_lock_unlock (syslog_lock);
} }
@ -169,9 +175,17 @@ __vsyslog_internal(int pri, const char *fmt, va_list ap,
pri &= LOG_PRIMASK|LOG_FACMASK; pri &= LOG_PRIMASK|LOG_FACMASK;
} }
/* Prepare for multiple users. We have to take care: most
syscalls we are using are cancellation points. */
struct cleanup_arg clarg;
clarg.buf = NULL;
clarg.oldaction = NULL;
__libc_cleanup_push (cancel_handler, &clarg);
__libc_lock_lock (syslog_lock);
/* Check priority against setlogmask values. */ /* Check priority against setlogmask values. */
if ((LOG_MASK (LOG_PRI (pri)) & LogMask) == 0) if ((LOG_MASK (LOG_PRI (pri)) & LogMask) == 0)
return; goto out;
/* Set default facility if none specified. */ /* Set default facility if none specified. */
if ((pri & LOG_FACMASK) == 0) if ((pri & LOG_FACMASK) == 0)
@ -235,6 +249,9 @@ __vsyslog_internal(int pri, const char *fmt, va_list ap,
/* Close the memory stream; this will finalize the data /* Close the memory stream; this will finalize the data
into a malloc'd buffer in BUF. */ into a malloc'd buffer in BUF. */
fclose (f); fclose (f);
/* Tell the cancellation handler to free this buffer. */
clarg.buf = buf;
} }
/* Output to stderr if requested. */ /* Output to stderr if requested. */
@ -252,22 +269,10 @@ __vsyslog_internal(int pri, const char *fmt, va_list ap,
v->iov_len = 1; v->iov_len = 1;
} }
__libc_cleanup_push (free, buf == failbuf ? NULL : buf);
/* writev is a cancellation point. */ /* writev is a cancellation point. */
(void)__writev(STDERR_FILENO, iov, v - iov + 1); (void)__writev(STDERR_FILENO, iov, v - iov + 1);
__libc_cleanup_pop (0);
} }
/* Prepare for multiple users. We have to take care: open and
write are cancellation points. */
struct cleanup_arg clarg;
clarg.buf = buf;
clarg.oldaction = NULL;
__libc_cleanup_push (cancel_handler, &clarg);
__libc_lock_lock (syslog_lock);
#ifndef NO_SIGPIPE #ifndef NO_SIGPIPE
/* Prepare for a broken connection. */ /* Prepare for a broken connection. */
memset (&action, 0, sizeof (action)); memset (&action, 0, sizeof (action));
@ -320,6 +325,7 @@ __vsyslog_internal(int pri, const char *fmt, va_list ap,
__sigaction (SIGPIPE, &oldaction, (struct sigaction *) NULL); __sigaction (SIGPIPE, &oldaction, (struct sigaction *) NULL);
#endif #endif
out:
/* End of critical section. */ /* End of critical section. */
__libc_cleanup_pop (0); __libc_cleanup_pop (0);
__libc_lock_unlock (syslog_lock); __libc_lock_unlock (syslog_lock);
@ -430,8 +436,14 @@ setlogmask (int pmask)
{ {
int omask; int omask;
/* Protect against multiple users. */
__libc_lock_lock (syslog_lock);
omask = LogMask; omask = LogMask;
if (pmask != 0) if (pmask != 0)
LogMask = pmask; LogMask = pmask;
__libc_lock_unlock (syslog_lock);
return (omask); return (omask);
} }