📄 sendmsg.c
字号:
/* If it's an object-manipulation message get the attribute's mandatory
ACL; if it's an object-parameter message get the parameter's mandatory
ACL. Since these doesn't require access to any object information, we
can do it before we lock the object table */
if( isAttributeMessage( localMessage ) )
{
attributeACL = findAttributeACL( messageValue, isInternalMessage );
if( attributeACL == NULL )
return( CRYPT_ARGERROR_VALUE );
aclPtr = attributeACL;
}
if( isParamMessage( localMessage ) )
{
aclPtr = findParamACL( localMessage );
ENSURES( aclPtr != NULL );
}
/* Inner precondition: If it's an attribute-manipulation message, we have
a valid ACL for the attribute present */
PRE( !isAttributeMessage( localMessage ) || attributeACL != NULL );
/* If we're in the middle of a shutdown, don't allow any further
messages except ones related to object destruction (the status read
is needed for objects capable of performing async ops, since the
shutdown code needs to determine whether they're currently busy).
The check outside the object-table lock is done in order to have any
remaining active objects exit quickly without tying up the object
table, since we don't want them to block the shutdown. In addition
if the thread is a leftover/long-running thread that's still active
after the shutdown has occurred, we can't access the object table
lock since it'll have been deleted */
if( krnlData->shutdownLevel >= SHUTDOWN_LEVEL_MESSAGES && \
!( localMessage == MESSAGE_DESTROY || \
localMessage == MESSAGE_DECREFCOUNT || \
( localMessage == MESSAGE_GETATTRIBUTE && \
messageValue == CRYPT_IATTRIBUTE_STATUS ) ) )
{
/* Exit without even trying to acquire the object table lock */
return( CRYPT_ERROR_PERMISSION );
}
/* Lock the object table to ensure that other threads don't try to
access it */
MUTEX_LOCK( objectTable );
objectTable = krnlData->objectTable;
/* The first line of defence: Make sure that the message is being sent
to a valid object and that the object is externally visible and
accessible to the caller if required by the message. The checks
performed are:
if( handle does not correspond to an object )
error;
if( message is external )
{
if( object is internal )
error;
if( object isn't owned by calling thread )
error;
}
This is equivalent to the shorter form fullObjectCheck() that used
elsewhere. The error condition reported in all of these cases is
that the object handle isn't valid */
if( !isValidObject( objectHandle ) )
status = CRYPT_ARGERROR_OBJECT;
else
{
if( !isInternalMessage && \
( isInternalObject( objectHandle ) || \
!checkObjectOwnership( objectTable[ objectHandle ] ) ) )
status = CRYPT_ARGERROR_OBJECT;
}
if( cryptStatusError( status ) )
{
MUTEX_UNLOCK( objectTable );
return( status );
}
/* Inner precondition now that the outer check has been passed: It's a
valid, accessible object and not a system object that can never be
explicitly destroyed or have its refCount altered */
PRE( isValidObject( objectHandle ) );
PRE( isInternalMessage || ( !isInternalObject( objectHandle ) && \
checkObjectOwnership( objectTable[ objectHandle ] ) ) );
PRE( fullObjectCheck( objectHandle, message ) );
PRE( objectHandle >= NO_SYSTEM_OBJECTS || \
( localMessage != MESSAGE_DESTROY && \
localMessage != MESSAGE_DECREFCOUNT && \
localMessage != MESSAGE_INCREFCOUNT ) );
/* If this message is routable, find its target object */
if( handlingInfoPtr->routingFunction != NULL )
{
/* If it's implicitly routed, route it based on the attribute type */
if( isImplicitRouting( handlingInfoPtr->routingTarget ) )
{
if( attributeACL->routingFunction != NULL )
localObjectHandle = attributeACL->routingFunction( objectHandle,
attributeACL->routingTarget );
}
else
{
/* It's explicitly or directly routed, route it based on the
message type or fixed-target type */
localObjectHandle = handlingInfoPtr->routingFunction( objectHandle,
isExplicitRouting( handlingInfoPtr->routingTarget ) ? \
messageValue : handlingInfoPtr->routingTarget );
}
if( cryptStatusError( localObjectHandle ) )
{
MUTEX_UNLOCK( objectTable );
return( CRYPT_ARGERROR_OBJECT );
}
}
/* Inner precodition: It's a valid destination object */
PRE( isValidObject( localObjectHandle ) );
/* Sanity-check the message routing */
if( !isValidObject( localObjectHandle ) )
{
MUTEX_UNLOCK( objectTable );
retIntError();
}
/* It's a valid object, get its info */
objectInfoPtr = &objectTable[ localObjectHandle ];
/* Now that the message has been routed to its intended target, make sure
that it's valid for the target object subtype */
if( !isValidSubtype( handlingInfoPtr->subTypeA, objectInfoPtr->subType ) && \
!isValidSubtype( handlingInfoPtr->subTypeB, objectInfoPtr->subType ) )
{
MUTEX_UNLOCK( objectTable );
return( CRYPT_ARGERROR_OBJECT );
}
/* Inner precondition: The message is valid for this object subtype */
PRE( isValidSubtype( handlingInfoPtr->subTypeA, objectInfoPtr->subType ) || \
isValidSubtype( handlingInfoPtr->subTypeB, objectInfoPtr->subType ) );
/* If this message is processed internally, handle it now. These
messages aren't affected by the object's state so they're always
processed */
if( handlingInfoPtr->internalHandlerFunction != NULL || \
( attributeACL != NULL && \
attributeACL->flags & ATTRIBUTE_FLAG_PROPERTY ) )
{
status = processInternalMessage( localObjectHandle, handlingInfoPtr,
message, messageDataPtr,
messageValue, aclPtr );
if( status != OK_SPECIAL )
{
/* The message was processed normally, exit */
MUTEX_UNLOCK( objectTable );
return( status );
}
/* The object has entered an invalid state (for example it was
signalled while it was being initialised) and can't be used any
more, destroy it, convert the (local copy of the) message into a
destroy object message */
localMessage = MESSAGE_DESTROY;
status = CRYPT_OK;
}
/* If the object isn't already processing a message and the message isn't
a special type such as MESSAGE_DESTROY, dispatch it immediately rather
than enqueueing it for later dispatch. This scoreboard mechanism
greatly reduces the load on the queue */
if( !isInUse( localObjectHandle ) && localMessage != MESSAGE_DESTROY )
{
CONST_INIT_STRUCT_5( MESSAGE_QUEUE_DATA messageQueueData, \
localObjectHandle, handlingInfoPtr, message, \
messageDataPtr, messageValue );
CONST_SET_STRUCT( messageQueueData.objectHandle = localObjectHandle; \
messageQueueData.handlingInfoPtr = handlingInfoPtr; \
messageQueueData.message = message; \
messageQueueData.messageDataPtr = messageDataPtr; \
messageQueueData.messageValue = messageValue );
/* If the object isn't in a valid state, we can't do anything with it.
There are no messages that can be sent to it at this point, get/
set property messages have already been handled earlier and the
destroy message isn't handled here */
if( isInvalidObjectState( localObjectHandle ) )
{
status = getObjectStatusValue( objectInfoPtr->flags );
MUTEX_UNLOCK( objectTable );
return( status );
}
/* In case a shutdown was signalled while we were performing other
processing, exit now before we try and do anything with the
object. It's safe to perform the check at this point since no
message sent during shutdown will get here */
if( krnlData->shutdownLevel >= SHUTDOWN_LEVEL_MESSAGES )
{
MUTEX_UNLOCK( objectTable );
return( CRYPT_ERROR_PERMISSION );
}
/* Inner precondition: The object is in a valid state */
PRE( !isInvalidObjectState( localObjectHandle ) );
/* Dispatch the message to the object */
status = dispatchMessage( localObjectHandle, &messageQueueData,
objectInfoPtr, aclPtr );
MUTEX_UNLOCK( objectTable );
/* If it's a zeroise, perform a kernel shutdown. In theory we could
do this from the post-dispatch handler, but we need to make sure
that there are no further kernel actions to be taken before we
perform the shutdown, so we do it at this level instead */
if( cryptStatusOK( status ) && \
( messageQueueData.message & MESSAGE_MASK ) == MESSAGE_USER_USERMGMT && \
messageQueueData.messageValue == MESSAGE_USERMGMT_ZEROISE )
{
endCryptlib();
}
/* Postcondition: The return status is valid */
POST( cryptStandardError( status ) || cryptArgError( status ) || \
status == OK_SPECIAL );
return( status );
}
/* Inner precondition: The object is in use or it's a destroy object
message, we have to enqueue it */
PRE( isInUse( localObjectHandle ) || localMessage == MESSAGE_DESTROY );
/* If we're stuck in a loop processing recursive messages, bail out.
This would happen automatically anyway once we fill the message queue,
but this early-out mechanism prevents a single object from filling the
queue to the detriment of other objects */
if( objectInfoPtr->lockCount > MESSAGE_QUEUE_SIZE / 2 )
{
MUTEX_UNLOCK( objectTable );
assert( DEBUG_WARN );
return( CRYPT_ERROR_TIMEOUT );
}
/* If the object is in use by another thread, wait for it to become
available */
if( isInUse( localObjectHandle ) && !isObjectOwner( localObjectHandle ) )
{
status = waitForObject( localObjectHandle, &objectInfoPtr );
#if !defined( NDEBUG ) && defined( USE_THREADS )
if( cryptStatusOK( status ) && isInUse( localObjectHandle ) )
{
/* dispatchMessage() expects us to be the lock owner if the
object is in use, however if the object has been */
objectInfoPtr->lockOwner = THREAD_SELF();
}
#endif /* !NDEBUG && USE_THREADS */
}
if( cryptStatusError( status ) )
{
MUTEX_UNLOCK( objectTable );
return( status );
}
assert( !isInUse( localObjectHandle ) || \
isObjectOwner( localObjectHandle ) );
/* Enqueue the message */
if( ( message & MESSAGE_MASK ) != localMessage )
{
/* The message was converted during processing, this can only happen
when a message sent to an invalid-state object is converted into
a destroy-object message. What we therefore enqueue is a
destroy-object message, but with the messageValue parameter set
to TRUE to indicate that it's a converted destroy message */
PRE( localMessage == MESSAGE_DESTROY );
status = enqueueMessage( localObjectHandle,
&messageHandlingInfo[ MESSAGE_DESTROY ],
MESSAGE_DESTROY, messageDataPtr, TRUE );
}
else
{
status = enqueueMessage( localObjectHandle, handlingInfoPtr, message,
messageDataPtr, messageValue );
}
if( cryptStatusError( status ) )
{
/* A message for this object is already present in the queue, defer
processing until later */
MUTEX_UNLOCK( objectTable );
return( ( status == OK_SPECIAL ) ? CRYPT_OK : status );
}
assert( !isInUse( localObjectHandle ) || \
isObjectOwner( localObjectHandle ) );
/* While there are more messages for this object present, dequeue them
and dispatch them. Since messages will only be enqueued if
krnlSendMessage() is called recursively, we only dequeue messages for
the current object in this loop. Queued messages for other objects
will be handled at a different level of recursion.
Bounding this loop is a bit tricky, since new messages can arrive as
the existing ones are dequeued, so that in theory the arrival rate
could match the dispatch rate. However in practice a situation like
this would be extremely unusual, so we bound the loop at
FAILSAFE_ITERATIONS_LARGE */
while( getNextMessage( localObjectHandle, &enqueuedMessageData ) && \
iterationCount++ < FAILSAFE_ITERATIONS_LARGE )
{
const BOOLEAN isDestroy = \
( enqueuedMessageData.message & MESSAGE_MASK ) == MESSAGE_DESTROY;
const BOOLEAN isZeroise = \
( enqueuedMessageData.message & MESSAGE_MASK ) == MESSAGE_USER_USERMGMT && \
enqueuedMe
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -