/****************************************************************************
*																			*
*							Semaphores and Mutexes							*
*						Copyright Peter Gutmann 1997-2016					*
*																			*
****************************************************************************/

#if defined( INC_ALL )
  #include "crypt.h"
  #include "acl.h"
  #include "kernel.h"
#else
  #include "crypt.h"
  #include "kernel/acl.h"
  #include "kernel/kernel.h"
#endif /* Compiler-specific includes */

/****************************************************************************
*																			*
*							Init/Shutdown Functions							*
*																			*
****************************************************************************/

/* A template to initialise the semaphore table */

static const SEMAPHORE_INFO SEMAPHORE_INFO_TEMPLATE = \
				{ SEMAPHORE_STATE_UNINITED, 0, 0 };

/* Create and destroy the semaphores and mutexes.  Since mutexes usually 
   aren't scalar values and are declared and accessed via macros that 
   manipulate various fields, we have to handle a pile of them individually 
   rather than using an array of mutexes */

CHECK_RETVAL \
int initSemaphores( void )
	{
	KERNEL_DATA *krnlData = getSystemStorage( SYSTEM_STORAGE_KRNLDATA );
	LOOP_INDEX i;
	int status;

#if defined( USE_SESSIONS )
	static_assert( MUTEX_LAST == 6, "Mutex value" );
#elif defined( USE_TCP )
	static_assert( MUTEX_LAST == 4, "Mutex value" );
#else
	static_assert( MUTEX_LAST == 3, "Mutex value" );
#endif /* USE_SESSIONS */

	/* Clear the semaphore table */
	LOOP_SMALL( i = 0, i < SEMAPHORE_LAST, i++ )
		{
		ENSURES( LOOP_INVARIANT_SMALL( i, 0, SEMAPHORE_LAST - 1 ) );

		krnlData->semaphoreInfo[ i ] = SEMAPHORE_INFO_TEMPLATE;
		}
	ENSURES( LOOP_BOUND_OK );

	/* Initialize any data structures required to make the semaphore table
	   thread-safe */
	MUTEX_CREATE( semaphore, status );
	ENSURES( cryptStatusOK( status ) );

	/* Initialize the mutexes */
	MUTEX_CREATE( mutex1, status );
	ENSURES( cryptStatusOK( status ) );
	MUTEX_CREATE( mutex2, status );
	ENSURES( cryptStatusOK( status ) );
#ifdef USE_SESSIONS
	MUTEX_CREATE( mutex3, status );
	ENSURES( cryptStatusOK( status ) );
	MUTEX_CREATE( mutex4, status );
	ENSURES( cryptStatusOK( status ) );
	MUTEX_CREATE( mutex5, status );
	ENSURES( cryptStatusOK( status ) );
	MUTEX_CREATE( mutex6, status );
	ENSURES( cryptStatusOK( status ) );
#endif /* USE_SESSIONS */

	return( CRYPT_OK );
	}

void endSemaphores( void )
	{
	KERNEL_DATA *krnlData = getSystemStorage( SYSTEM_STORAGE_KRNLDATA );

	REQUIRES_V( ( krnlData->initLevel == INIT_LEVEL_KRNLDATA && \
				  krnlData->shutdownLevel == SHUTDOWN_LEVEL_NONE ) || \
				( krnlData->initLevel == INIT_LEVEL_KRNLDATA && \
				  krnlData->shutdownLevel == SHUTDOWN_LEVEL_MESSAGES ) || \
				( krnlData->initLevel == INIT_LEVEL_FULL && \
				  krnlData->shutdownLevel >= SHUTDOWN_LEVEL_MESSAGES ) );

	/* Signal that kernel mechanisms are no longer available */
	krnlData->shutdownLevel = SHUTDOWN_LEVEL_MUTEXES;

	/* Shut down the mutexes */
#ifdef USE_SESSIONS
	MUTEX_DESTROY( mutex6 );
	MUTEX_DESTROY( mutex5 );
	MUTEX_DESTROY( mutex4 );
	MUTEX_DESTROY( mutex3 );
#endif /* USE_SESSIONS */
	MUTEX_DESTROY( mutex2 );
	MUTEX_DESTROY( mutex1 );

	/* Destroy any data structures required to make the semaphore table
	   thread-safe */
	MUTEX_DESTROY( semaphore );
	}

/****************************************************************************
*																			*
*							Semaphore Functions								*
*																			*
****************************************************************************/

#ifdef USE_THREAD_FUNCTIONS

/* Under multithreaded OSes, we often need to wait for certain events before
   we can continue (for example when asynchronously accessing system
   objects anything that depends on the object being available needs to
   wait for the access to complete) or handle mutual exclusion when accessing
   a shared resource.  The following functions abstract this handling,
   providing a lightweight semaphore mechanism via mutexes, which is used 
   before checking a system synchronisation object (mutexes usually don't
   require a kernel entry, while semaphores usually do).  The semaphore 
   function works a bit like the Win32 Enter/LeaveCriticalSection() 
   routines, which perform a quick check on a user-level lock and only call 
   the kernel-level handler if necessary (in most cases this isn't 
   necessary).  A useful side-effect is that since they work with 
   lightweight local locks instead of systemwide locking objects, they 
   aren't vulnerable to security problems where (for example) another 
   process can mess with a globally visible object handle.  This is 
   particularly problematic under Windows, where (for example) CreateMutex()
   can return a handle to an already-existing object of the same name rather
   than a newly-created object (there's no O_EXCL functionality).

   Semaphores are one-shots, so that once set and cleared they can't be
   reset.  This is handled by enforcing the following state transitions:

	Uninited -> Set | Clear
	Set -> Set | Clear
	Clear -> Clear

   The handling is complicated somewhat by the fact that on some systems the
   semaphore has to be explicitly deleted, but only the last thread to use it
   can safely delete it.  In order to handle this, we reference-count the
   semaphore and let the last thread out delete it.  This is handled by
   introducing an additional state preClear, which indicates that while the
   semaphore object is still present, the last thread out should delete it,
   bringing it to the true clear state */

void setSemaphore( IN_ENUM( SEMAPHORE ) const SEMAPHORE_TYPE semaphore,
				   const MUTEX_HANDLE semaphoreObject,
				   const THREAD_HANDLE threadObject )
	{
	KERNEL_DATA *krnlData = getSystemStorage( SYSTEM_STORAGE_KRNLDATA );
	SEMAPHORE_INFO *semaphoreInfo;

	REQUIRES_V( isEnumRange( semaphore, SEMAPHORE ) );

	/* It's safe to get a pointer to this outside the lock, we just can't
	   access it yet */
	semaphoreInfo = &krnlData->semaphoreInfo[ semaphore ];

	/* Lock the semaphore table, set the semaphore, and unlock it again */
	MUTEX_LOCK( semaphore );
	if( semaphoreInfo->state == SEMAPHORE_STATE_UNINITED )
		{
		/* The semaphore can only be set if it's currently in the uninited 
		   state.  Since the semaphore handle may be a non-scalar type we
		   have to use a cast to make it non-const (semaphoreInfo->object
		   can be const if it's a pointer but has to be non-const if it's
		   a scalar) */
		*semaphoreInfo = SEMAPHORE_INFO_TEMPLATE;
		semaphoreInfo->state = SEMAPHORE_STATE_SET;
		semaphoreInfo->semaphoreObject = semaphoreObject;
		semaphoreInfo->threadObject = threadObject;
		}
	MUTEX_UNLOCK( semaphore );
	}

void clearSemaphore( IN_ENUM( SEMAPHORE ) const SEMAPHORE_TYPE semaphore )
	{
	KERNEL_DATA *krnlData = getSystemStorage( SYSTEM_STORAGE_KRNLDATA );
	SEMAPHORE_INFO *semaphoreInfo;

	REQUIRES_V( isEnumRange( semaphore, SEMAPHORE ) );

	/* It's safe to get a pointer to this outside the lock, we just can't
	   access it yet */
	semaphoreInfo = &krnlData->semaphoreInfo[ semaphore ];

	/* Lock the semaphore table, clear the semaphore, and unlock it again */
	MUTEX_LOCK( semaphore );
	if( semaphoreInfo->state == SEMAPHORE_STATE_SET )
		{
		/* Precondition: The reference count is valid.  Note that we have to
		   make this an assert() rather than a REQUIRES() because the latter
		   would exit with the semaphore still held */
#if !( defined( __WINCE__ ) && _WIN32_WCE < 400 )
		assert( semaphoreInfo[ semaphore ].refCount >= 0 );
#endif /* Fix for bug in PocketPC 2002 emulator with eVC++ 3.0 */

		/* If there are threads waiting on this semaphore, tell the last
		   thread out to turn out the lights */
		if( semaphoreInfo->refCount > 0 )
			semaphoreInfo->state = SEMAPHORE_STATE_PRECLEAR;
		else
			{
			/* No threads waiting on the semaphore, we can delete it */
			THREAD_CLOSE( semaphoreInfo->threadObject );
			*semaphoreInfo = SEMAPHORE_INFO_TEMPLATE;
			}
		}
	MUTEX_UNLOCK( semaphore );
	}

/* Wait on a semaphore.  This occurs in two phases, first we extract the
   information that we need from the semaphore table, then we unlock it and 
   wait on the semaphore if necessary.  This is necessary because the wait 
   can take an indeterminate amount of time and we don't want to tie up the 
   other semaphores while this occurs.  Note that this type of waiting on 
   local (rather than system) semaphores where possible greatly improves
   performance, in some cases the wait on a signalled system semaphore can
   take several seconds whereas waiting on the local semaphore only takes a
   few ms.  Once the wait has completed, we update the semaphore state as
   per the longer description above */

CHECK_RETVAL_BOOL \
BOOLEAN krnlWaitSemaphore( IN_ENUM( SEMAPHORE ) const SEMAPHORE_TYPE semaphore )
	{
	KERNEL_DATA *krnlData = getSystemStorage( SYSTEM_STORAGE_KRNLDATA );
	SEMAPHORE_INFO *semaphoreInfo;
#ifdef NONSCALAR_HANDLES
	MUTEX_HANDLE *semaphoreObject DUMMY_INIT_PTR;
	THREAD_HANDLE *threadObject DUMMY_INIT_PTR;
#else
	MUTEX_HANDLE semaphoreObject DUMMY_INIT_MUTEX;
	THREAD_HANDLE threadObject DUMMY_INIT_THREAD;
#endif /* NONSCALAR_HANDLES */
	BOOLEAN semaphoreSet = FALSE;
	int status = CRYPT_OK;

	REQUIRES_B( isEnumRange( semaphore, SEMAPHORE ) );

	/* If we're in a shutdown and the semaphores have been destroyed, don't
	   try and acquire the semaphore mutex.  In this case anything that 
	   they're protecting should be set to a shutdown state in which any 
	   access fails, so this isn't a problem */
	if( krnlData->shutdownLevel >= SHUTDOWN_LEVEL_MUTEXES )
		return( FALSE );

	/* Lock the semaphore table, extract the information that we need, and 
	   unlock it again */
	semaphoreInfo = &krnlData->semaphoreInfo[ semaphore ];
	MUTEX_LOCK( semaphore );
	if( semaphoreInfo->state == SEMAPHORE_STATE_SET )
		{
		/* Precondition: The reference count is valid.  Note that we have to
		   make this an assert() rather than a REQUIRES() because the latter
		   would exit with the semaphore still held */
		assert( semaphoreInfo->refCount >= 0 );

		/* The semaphore is set and not in use, extract the information we
		   require and mark is as being in use */
#ifdef NONSCALAR_HANDLES
		semaphoreObject = &semaphoreInfo->semaphoreObject;
		threadObject = &semaphoreInfo->threadObject;
#else
		semaphoreObject = semaphoreInfo->semaphoreObject;
		threadObject = semaphoreInfo->threadObject;
#endif /* NONSCALAR_HANDLES */
		semaphoreInfo->refCount++;
		semaphoreSet = TRUE;
		}
	MUTEX_UNLOCK( semaphore );

	/* If the semaphore wasn't set or is in use, exit now */
	if( !semaphoreSet )
		return( TRUE );

	/* Wait on the object */
#ifdef NONSCALAR_HANDLES
	assert( memcmp( semaphoreObject, 
					&SEMAPHORE_INFO_TEMPLATE.semaphoreObject,
					sizeof( MUTEX_HANDLE ) ) );
#else
	assert( memcmp( &semaphoreObject, 
					&SEMAPHORE_INFO_TEMPLATE.semaphoreObject,
					sizeof( MUTEX_HANDLE ) ) );
#endif /* NONSCALAR_HANDLES */
	THREAD_WAIT( threadObject, semaphoreObject, status );
	if( cryptStatusError( status ) )
		{
		DEBUG_DIAG(( "Wait on object failed" ));
		assert( DEBUG_WARN );
		return( FALSE );
		}

	/* Lock the semaphore table, update the information, and unlock it
	   again */
	MUTEX_LOCK( semaphore );
	if( semaphoreInfo->state == SEMAPHORE_STATE_SET || \
		semaphoreInfo->state == SEMAPHORE_STATE_PRECLEAR )
		{
		/* The semaphore is still set, update the reference count */
		semaphoreInfo->refCount--;

		/* Inner precondition: The reference count is valid.  Note that we 
		   have to make this an assert() rather than a REQUIRES() because 
		   the latter would exit with the semaphore still held */
		assert( semaphoreInfo->refCount >= 0 );

		/* If the object owner has signalled that it's done with the object
		   and the reference count has reached zero, we can delete it */
		if( semaphoreInfo->state == SEMAPHORE_STATE_PRECLEAR || \
			semaphoreInfo->refCount <= 0 )
			{
			/* No threads waiting on the semaphore, we can delete it */
			THREAD_CLOSE( threadObject );
			*semaphoreInfo = SEMAPHORE_INFO_TEMPLATE;
			}
		}
	MUTEX_UNLOCK( semaphore );
	
	return( TRUE );
	}
#else

/* krnlWaitSemaphore() is only used if threading functions are enabled, 
   however this is an externally-visible API so we need to provide a dummy
   replacement for it as opposed to the kernel-internal setSemaphore()/
   clearSemaphore() which can be relaced with a #define */ 

CHECK_RETVAL_BOOL \
BOOLEAN krnlWaitSemaphore( IN_ENUM( SEMAPHORE ) const SEMAPHORE_TYPE semaphore )
	{
	return( TRUE );
	}
#endif /* USE_THREAD_FUNCTIONS */

/****************************************************************************
*																			*
*								Mutex Functions								*
*																			*
****************************************************************************/

/* Enter and exit a mutex */

CHECK_RETVAL \
int krnlEnterMutex( IN_ENUM( MUTEX ) const MUTEX_TYPE mutex )
	{
	KERNEL_DATA *krnlData = getSystemStorage( SYSTEM_STORAGE_KRNLDATA );

	REQUIRES( isEnumRange( mutex, MUTEX ) );

	/* If we're in a shutdown and the mutexes have been destroyed, don't
	   try and acquire them.  In this case anything that they're protecting
	   should be set to a shutdown state in which any access fails, so this
	   isn't a problem */
	if( krnlData->shutdownLevel >= SHUTDOWN_LEVEL_MUTEXES )
		return( CRYPT_ERROR_PERMISSION );

	switch( mutex )
		{
		case MUTEX_RANDOM:
			MUTEX_LOCK( mutex1 );
			break;

		case MUTEX_RANDOMNONCE:
			MUTEX_LOCK( mutex2 );
			break;

#ifdef USE_SESSIONS
		case MUTEX_SCOREBOARD:
			MUTEX_LOCK( mutex3 );
			break;

		case MUTEX_SOCKETPOOL:
			MUTEX_LOCK( mutex4 );
			break;

		case MUTEX_CRYPTODELAY:
			MUTEX_LOCK( mutex5 );
			break;
#endif /* USE_SESSIONS */

		default:
			retIntError();
		}

	return( CRYPT_OK );
	}

void krnlExitMutex( IN_ENUM( MUTEX ) const MUTEX_TYPE mutex )
	{
	KERNEL_DATA *krnlData = getSystemStorage( SYSTEM_STORAGE_KRNLDATA );

	REQUIRES_V( isEnumRange( mutex, MUTEX ) );

	/* If we're in a shutdown and the mutexes have been destroyed, don't
	   try and acquire them.  In this case anything that they're protecting
	   should be set to a shutdown state in which any access fails, so this
	   isn't a problem */
	if( krnlData->shutdownLevel >= SHUTDOWN_LEVEL_MUTEXES )
		return;

	switch( mutex )
		{
		case MUTEX_RANDOM:
			MUTEX_UNLOCK( mutex1 );
			break;

		case MUTEX_RANDOMNONCE:
			MUTEX_UNLOCK( mutex2 );
			break;

#ifdef USE_SESSIONS
		case MUTEX_SCOREBOARD:
			MUTEX_UNLOCK( mutex3 );
			break;

		case MUTEX_SOCKETPOOL:
			MUTEX_UNLOCK( mutex4 );
			break;

		case MUTEX_CRYPTODELAY:
			MUTEX_UNLOCK( mutex5 );
			break;
#endif /* USE_SESSIONS */

		default:
			retIntError_Void();
		}
	}
