/********************************************************/ /* ntapi: Native API core library */ /* Copyright (C) 2013--2017 Z. Gilboa */ /* Released under GPLv2 and GPLv3; see COPYING.NTAPI. */ /********************************************************/ #include #include #include #include #include #include "ntapi_impl.h" static void __sync_block_memset( __in nt_sync_block * sync_block, __in intptr_t value) { intptr_t * sptr = sync_block->cache_line; at_store(&sptr[0x0],value); at_store(&sptr[0x1],value); at_store(&sptr[0x2],value); at_store(&sptr[0x3],value); at_store(&sptr[0x4],value); at_store(&sptr[0x5],value); at_store(&sptr[0x6],value); at_store(&sptr[0x7],value); if (sizeof(intptr_t) == 4) { at_store(&sptr[0x8],value); at_store(&sptr[0x9],value); at_store(&sptr[0xa],value); at_store(&sptr[0xb],value); at_store(&sptr[0xc],value); at_store(&sptr[0xd],value); at_store(&sptr[0xe],value); at_store(&sptr[0xf],value); } } void __stdcall __ntapi_tt_sync_block_init( __in nt_sync_block * sync_block, __in uint32_t flags __optional, __in int32_t srvtid __optional, __in int32_t default_lock_tries __optional, __in int64_t default_lock_wait __optional, __in void * hsignal __optional) { __sync_block_memset( sync_block,0); at_store_32( &sync_block->lock_tries, default_lock_tries ? default_lock_tries : __NT_SYNC_BLOCK_LOCK_TRIES); at_store_64( &sync_block->lock_wait.quad, default_lock_wait ? default_lock_wait : (-1)); at_store_32( (int32_t *)&sync_block->flags, flags); at_store_32( (int32_t *)&sync_block->srvtid, srvtid); at_store( (intptr_t *)&sync_block->hsignal, (intptr_t)hsignal); } int32_t __stdcall __ntapi_tt_sync_block_lock( __in nt_sync_block * sync_block, __in int32_t lock_tries __optional, __in int64_t lock_wait __optional, __in uint32_t * sig_flag __optional) { int32_t status; int32_t tid; intptr_t lock; void * hwait[2]; nt_timeout timeout; /* validation */ if (sync_block->invalid) return NT_STATUS_INVALID_HANDLE; /* already owned? */ if ((tid = pe_get_current_thread_id()) == sync_block->tid) return NT_STATUS_SUCCESS; /* yield to server? */ if ((sync_block->flags & NT_SYNC_BLOCK_YIELD_TO_SERVER) && ((uint32_t)tid != sync_block->srvtid)) { hwait[0] = sync_block->hserver; hwait[1] = sync_block->hsignal; /* signal support */ if (sig_flag && *sig_flag) return NT_STATUS_ALERTED; /* wait */ status = __ntapi->zw_wait_for_multiple_objects( 2,hwait, NT_WAIT_ANY, NT_SYNC_NON_ALERTABLE, 0); /* signal support */ if (sig_flag && *sig_flag) return NT_STATUS_ALERTED; } /* first try */ lock = at_locked_cas_32( &sync_block->tid, 0,tid); if (lock && (lock_tries == 1)) return NT_STATUS_NOT_LOCKED; /* first-time contended case? */ if (lock && !sync_block->hwait) { if ((status = __ntapi->tt_create_inheritable_event( &hwait[0], NT_NOTIFICATION_EVENT, NT_EVENT_NOT_SIGNALED))) return status; lock = at_locked_cas( (intptr_t *)&sync_block->hwait, 0,(intptr_t)hwait[0]); if (lock) __ntapi->zw_close(hwait[0]); /* try again without a wait */ lock = at_locked_cas_32( &sync_block->tid, 0,tid); } /* contended case? */ if (lock) { hwait[0] = sync_block->hwait; hwait[1] = sync_block->hsignal; lock_tries = lock_tries ? lock_tries : sync_block->lock_tries; timeout.quad = lock_wait ? lock_wait : sync_block->lock_wait.quad; for (; lock && lock_tries; lock_tries--) { /* signal support */ if (sig_flag && *sig_flag) return NT_STATUS_ALERTED; /* wait */ status = __ntapi->zw_wait_for_multiple_objects( 2,hwait, NT_WAIT_ANY, NT_SYNC_NON_ALERTABLE, &timeout); /* check status */ if ((status >= NT_STATUS_WAIT_0) && (status < NT_STATUS_WAIT_CAP)) (void)0; else if (status == NT_STATUS_TIMEOUT) (void)0; else return status; /* signal support */ if (sig_flag && *sig_flag) return NT_STATUS_ALERTED; /* try again */ lock = at_locked_cas_32( &sync_block->tid, 0,tid); }; } if (lock) return NT_STATUS_NOT_LOCKED; /* shared section support */ at_store_32( &sync_block->pid, pe_get_current_process_id()); return NT_STATUS_SUCCESS; } int32_t __stdcall __ntapi_tt_sync_block_server_lock( __in nt_sync_block * sync_block, __in int32_t lock_tries __optional, __in int64_t lock_wait __optional, __in uint32_t * sig_flag __optional) { int32_t status; /* validation */ if (sync_block->invalid) return NT_STATUS_INVALID_HANDLE; else if (sync_block->srvtid != pe_get_current_thread_id()) return NT_STATUS_RESOURCE_NOT_OWNED; /* try once without yield request */ status = __ntapi_tt_sync_block_lock( sync_block, 1,lock_wait, sig_flag); if (status == NT_STATUS_SUCCESS) return status; /* hserver */ if (!sync_block->hserver) { if ((status = __ntapi->tt_create_inheritable_event( &sync_block->hserver, NT_NOTIFICATION_EVENT, NT_EVENT_NOT_SIGNALED))) return status; } else { if ((status = __ntapi->zw_reset_event( sync_block->hserver,0))) return status; } /* yield request: set */ at_locked_or_32( (int32_t *)&sync_block->flags, NT_SYNC_BLOCK_YIELD_TO_SERVER); /* try again */ status = __ntapi_tt_sync_block_lock( sync_block, lock_tries, lock_wait, sig_flag); /* yield request: unset */ at_locked_xor_32( (int32_t *)&sync_block->flags, NT_SYNC_BLOCK_YIELD_TO_SERVER); __ntapi->zw_set_event( sync_block->hserver, 0); /* (locking not guaranteed) */ return status; } int32_t __stdcall __ntapi_tt_sync_block_unlock( __in nt_sync_block * sync_block) { union { int64_t i64; nt_large_integer nti64; } cmp; if (sync_block->invalid) return NT_STATUS_INVALID_HANDLE; cmp.nti64.ihigh = pe_get_current_process_id(); cmp.nti64.ulow = pe_get_current_thread_id(); if (cmp.i64 != at_locked_cas_64( (int64_t *)&sync_block->tid, cmp.i64,0)) return NT_STATUS_RESOURCE_NOT_OWNED; return NT_STATUS_SUCCESS; } int32_t __stdcall __ntapi_tt_sync_block_discard( __in nt_sync_block * sync_block) { if (sync_block->hwait) __ntapi->zw_close( sync_block->hwait); if (sync_block->hserver) __ntapi->zw_close( sync_block->hserver); __sync_block_memset( sync_block,-1); return NT_STATUS_SUCCESS; }