📄 os_spec.c
字号:
/****************************************************************************
* *
* RTEMS *
* *
****************************************************************************/
#elif defined( __RTEMS__ )
/* The RTEMS thread-self function returns the task ID via a reference
parameter, because of this we have to provide a wrapper that returns it
as a return value. We use RTEMS_SEARCH_ALL_NODES because there isn't
any other way to specify the local node, this option always searches the
local node first so it has the desired effect */
rtems_id threadSelf( void )
{
rtems_id taskID;
rtems_task_ident( RTEMS_SELF, RTEMS_SEARCH_ALL_NODES, &taskID );
return( taskID );
}
/****************************************************************************
* *
* Tandem *
* *
****************************************************************************/
/* The Tandem mktime() is broken and can't convert dates beyond 2023, if
mktime() fails and the year is between then and the epoch try again with
a time that it can convert */
#elif defined( __TANDEM_NSK__ ) || defined( __TANDEM_OSS__ )
#undef mktime /* Restore the standard mktime() */
time_t my_mktime( struct tm *timeptr )
{
time_t theTime;
theTime = mktime( timeptr );
if( theTime < 0 && timeptr->tm_year > 122 && timeptr->tm_year <= 138 )
{
timeptr->tm_year = 122; /* Try again with a safe year of 2022 */
theTime = mktime( timeptr );
}
return( theTime );
}
/****************************************************************************
* *
* Unix *
* *
****************************************************************************/
#elif defined( __UNIX__ ) && \
!( defined( __MVS__ ) || defined( __TANDEM_NSK__ ) || \
defined( __TANDEM_OSS__ ) )
#include <sys/time.h>
/* For performance evaluation purposes we provide the following function,
which returns ticks of the 1us timer */
long getTickCount( long startTime )
{
struct timeval tv;
long timeLSB, timeDifference;
/* Only accurate to about 1us */
gettimeofday( &tv, NULL );
timeLSB = tv.tv_usec;
/* If we're getting an initial time, return an absolute value */
if( startTime <= 0 )
return( timeLSB );
/* We're getting a time difference */
if( startTime < timeLSB )
timeDifference = timeLSB - startTime;
else
/* gettimeofday() rolls over at 1M us */
timeDifference = ( 1000000L - startTime ) + timeLSB;
if( timeDifference <= 0 )
{
printf( "Error: Time difference = %lX, startTime = %lX, "
"endTime = %lX.\n", timeDifference, startTime, timeLSB );
return( 1 );
}
return( timeDifference );
}
/* SunOS and older Slowaris have broken sprintf() handling. In SunOS 4.x
this was documented as returning a pointer to the output data as per the
Berkeley original. Under Slowaris the manpage was changed so that it
looks like any other sprintf(), but it still returns the pointer to the
output buffer in some versions so we use a wrapper that checks at
runtime to see what we've got and adjusts its behaviour accordingly. In
fact it's much easier to fix than that, since we have to use vsprintf()
anyway and this doesn't have the sprintf() problem, this fixes itself
simply from the use of the wrapper (unfortunately we can't use
vsnprintf() because these older OS versions don't include it yet) */
#if defined( sun ) && ( OSVERSION <= 5 )
#include <stdarg.h>
int fixedSprintf( char *buffer, const int bufSize, const char *format, ... )
{
va_list argPtr;
int length;
va_start( argPtr, format );
length = vsprintf( buffer, format, argPtr );
va_end( argPtr );
return( length );
}
#endif /* Old SunOS */
/****************************************************************************
* *
* Windows *
* *
****************************************************************************/
#elif defined( __WIN32__ )
/* Yielding a thread on an SMP or HT system is a tricky process,
particularly on an HT system. On an HT CPU the OS (or at least apps
running under the OS) think that there are two independent CPUs present,
but it's really just one CPU with partitioning of pipeline slots. So
when one thread yields, the only effect is that all of its pipeline slots
get marked as available. Since the other thread can't utilise those
slots, the first thread immediately reclaims them and continues to run.
In addition thread scheduling varies across OS versions, the WinXP
scheduler was changed to preferentially schedule threads on idle physical
processors rather than an idle logical processor on a physical processor
whose other logical processor is (potentially) busy.
There isn't really any easy way to fix this since it'd require a sleep
that works across all CPUs, however one solution is to make the thread
sleep for a nonzero time limit iff it's running on a multi-CPU system.
There's a second problem though, which relates to thread priorities. If
we're at a higher priority than the other thread then we can call
Sleep( 0 ) as much as we like, but the scheduler will never allow the
other thread to run since we're a higher-priority runnable thread. As a
result, as soon as we release our timeslice the scheduler will restart us
again (the Windows scheduler implements a starvation-prevention mechanism
via the balance set manager, but this varies across scheduler versions
and isn't something that we want to rely on). In theory we could do:
x = GetThreadPriority( GetCurrentThread() );
SetThreadPriority( GetCurrentThread(), x - 5 );
Sleep( 0 ); // Needed to effect priority change
<wait loop>
SetThreadPriority( GetCurrentThread(), x );
Sleep( 0 );
however this is somewhat problematic if the caller is also messing with
priorities at the same time. In fact it can get downright nasty because
the balance set manager will, if a thread has been starved for ~3-4
seconds, give it its own priority boost to priority 15 (time-critical) to
ensure that it'll be scheduled, with the priority slowly decaying back to
the normal level each time that it's scheduled. In addition it'll have
its scheduling quantum boosted to 2x the normal duration for a client OS
or 4x the normal duration for a server OS.
To solve this, we always force our thread to go to sleep (to allow a
potentially lower-priority thread to leap in and get some work done) even
on a single-processor system, but use a slightly longer wait on an
HT/multi-processor system.
(Actually this simplified view isn't quite accurate since on a HT system
the scheduler executes the top *two* threads on the two logical
processors and on a dual-CPU system they're executed on a physical
processor. In addition on a HT system a lower-priority thread on one
logical processor can compete with a higher-priority thread on the other
logical processor since the hardware isn't aware of thread priorities) */
void threadYield( void )
{
static int sleepTime = -1;
/* If the sleep time hasn't been determined yet, get it now */
if( sleepTime < 0 )
{
SYSTEM_INFO systemInfo;
GetSystemInfo( &systemInfo );
sleepTime = ( systemInfo.dwNumberOfProcessors > 1 ) ? 10 : 1;
}
/* Yield the CPU for this thread */
Sleep( sleepTime );
}
#ifndef NDEBUG
/* For performance evaluation purposes we provide the following function,
which returns ticks of the 3.579545 MHz hardware timer (see the long
comment in rndwin32.c for more details on Win32 timing issues) */
long getTickCount( long startTime )
{
long timeLSB, timeDifference;
#ifndef __BORLANDC__
LARGE_INTEGER performanceCount;
/* Sensitive to context switches */
QueryPerformanceCounter( &performanceCount );
timeLSB = performanceCount.LowPart;
#else
FILETIME dummyTime, kernelTime, userTime;
/* Only accurate to 10ms, returns constant values in VC++ debugger */
GetThreadTimes( GetCurrentThread(), &dummyTime, &dummyTime,
&kernelTime, &userTime );
timeLSB = userTime.dwLowDateTime;
#endif /* BC++ vs. everything else */
/* If we're getting an initial time, return an absolute value */
if( startTime <= 0 )
return( timeLSB );
/* We're getting a time difference */
if( startTime < timeLSB )
timeDifference = timeLSB - startTime;
else
{
/* Windows rolls over at INT_MAX */
timeDifference = ( 0xFFFFFFFFUL - startTime ) + 1 + timeLSB;
}
if( timeDifference <= 0 )
{
printf( "Error: Time difference = %X, startTime = %X, endTime = %X.\n",
timeDifference, startTime, timeLSB );
return( 1 );
}
return( timeDifference );
}
#endif /* Debug version */
/* Borland C++ before 5.50 doesn't have snprintf() so we fake it using
sprintf(). Unfortunately these are all va_args functions so we can't
just map them using macros but have to provide an explicit wrapper to get
rid of the size argument */
#if defined( __BORLANDC__ ) && ( __BORLANDC__ < 0x0550 )
int bcSnprintf( char *buffer, const int bufSize, const char *format, ... )
{
va_list argPtr;
int length;
va_start( argPtr, format );
length = vsprintf( buffer, format, argPtr );
va_end( argPtr );
return( length );
}
int bcVsnprintf( char *buffer, const int bufSize, const char *format, va_list argPtr )
{
return( vsprintf( buffer, format, argPtr ) );
}
#endif /* BC++ before 5.50 */
/* Safely load a DLL. This gets quite complicated because different
versions of Windows have changed how they search for DLLs to load, and
the behaviour of a specific version of Windows can be changed based on
registry keys and SetDllDirectory(). Traditionally Windows searched
the app directory, the current directory, the system directory, the
Windows directory, and the directories in $PATH. Windows XP SP2 added
the SafeDllSearchMode registry key, which changes the search order so
the current directory is searched towards the end rather than towards
the start, however it's (apparently) only set on new installs, on a
pre-SP2 install that's been upgraded it's not set. In addition
SetDllDirectory() can be used to add a new directory to the start of
the search order, or to revert to the default search order if it's
been changed previously.
None of these options are terribly useful if we want a DLL to either
be loaded from the system directory or not at all. To handle this we
build an absolute load path and prepend it to the name of the DLL
being loaded */
#ifndef CSIDL_SYSTEM
#define CSIDL_SYSTEM 0x25 /* 'Windows/System32' */
#endif /* !CSIDL_SYSTEM */
#ifndef SHGFP_TYPE_CURRENT
#define SHGFP_TYPE_CURRENT 0
#endif /* !SHGFP_TYPE_CURRENT */
HMODULE WINAPI SafeLoadLibrary( IN_STRING LPCTSTR lpFileName )
{
typedef HRESULT ( WINAPI *SHGETFOLDERPATH )( HWND hwndOwner,
int nFolder, HANDLE hToken,
DWORD dwFlags, LPTSTR lpszPath );
SHGETFOLDERPATH pSHGetFolderPath;
HINSTANCE hShell32;
char path[ MAX_PATH + 8 ];
const int fileNameLength = strlen( lpFileName ) + 1;
int pathLength;
BOOLEAN gotPath = FALSE;
/* If it's Win98 or NT4, just call LoadLibrary directly. In theory
we could try a few further workarounds (see io/file.c) but in
practice bending over backwards to fix search path issues under
Win98, which doesn't have ACLs to protect the files in the system
directory anyway, isn't going to achieve much */
if( getSysVar( SYSVAR_OSVERSION ) <= 4 )
return( LoadLibrary( lpFileName ) );
/* If it's already an absolute path, don't try and override it */
if( lpFileName[ 0 ] == '/' || \
( fileNameLength > 3 &&
lpFileName[ 1 ] == ':' && lpFileName[ 2 ] == '/' ) )
return( LoadLibrary( lpFileName ) );
/* It's a system new enough to support SHGetFolderPath(), get the path
to the system directory. Unfortunately at this point we're in a
catch-22, in order to resolve SHGetFolderPath() we need to call
Shell32.dll and if an attacker uses that as the injection point then
they can give us a SHGetFolderPath() that'll do whatever they want.
There's no real way to fix this because we have to load Shell32 at
some point, either explicitly here or on program load, and since we
can't control the load path at either point we can't control what's
actually being loaded. In addition DLLs typically recursively load
more DLLs so even if we can control the path of the DLL that we load
directly we can't influence the paths over which further DLLs get
loaded. So unfortunately the best that we can do is make the
attacker work a little harder rather than providing a full fix */
hShell32 = LoadLibrary( "Shell32.dll" );
pSHGetFolderPath = ( SHGETFOLDERPATH ) \
GetProcAddress( hShell32, "SHGetFolderPathA" );
if( pSHGetFolderPath != NULL && \
pSHGetFolderPath( NULL, CSIDL_SYSTEM, NULL, SHGFP_TYPE_CURRENT,
path ) == S_OK )
gotPath = TRUE;
FreeLibrary( hShell32 );
if( !gotPath )
{
/* If for some reason we couldn't get the path to the Windows system
directory (which would indicate there's something drastically
wrong), fall back to the default load */
return( LoadLibrary( lpFileName ) );
}
pathLength = strlen( path );
if( pathLength < 3 || pathLength + 1 + fileNameLength > MAX_PATH )
{
/* Under WinNT and Win2K the LocalSystem account doesn't have its
own profile so SHGetFolderPath() will report success but return a
zero-length path if we're running as a service so we have to
check for a valid-looking path as well as performing a general
check on the return status. In effect prepending a zero-length
path to the DLL name just turns the call into a standard
LoadLibrary() call, but we make the action explicit */
return( LoadLibrary( lpFileName ) );
}
path[ pathLength++ ] = '\\';
memcpy( path + pathLength, lpFileName, fileNameLength );
return( LoadLibrary( lpFileName ) );
}
/* Windows NT/2000/XP/Vista support ACL-based access control mechanisms for
system objects so when we create objects such as files and threads we
give them an ACL that allows only the creator access. The following
functions return the security info needed when creating objects. The
interface for this has changed in every major OS release, although it
never got any better, just differently ugly. The following code uses the
original NT 3.1 interface, which works for all OS versions */
/* The size of the buffer for ACLs and the user token */
#define ACL_BUFFER_SIZE 1024
#define TOKEN_BUFFER_SIZE 256
/* A composite structure to contain the various ACL structures. This is
required because ACL handling is a complex, multistage operation that
requires first creating an ACL and security descriptor to contain it,
adding an access control entry (ACE) to the ACL, adding the ACL as the
DACL of the security descriptor, and finally, wrapping the security
descriptor up in a security attributes structure that can be passed to
an object-creation function.
The handling of the TOKEN_INFO is extraordinarily ugly because although
the TOKEN_USER struct as defined is only 8 bytes long, Windoze allocates
an extra 24 bytes after the end of the struct into which it stuffs data
that the SID pointer in the TOKEN_USER struct points to. This means that
we can't statically allocate memory of the size of the TOKEN_USER struct
but have to make it a pointer into a larger buffer that can contain the
additional invisible data tacked onto the end */
typedef struct {
SECURITY_ATTRIBUTES sa;
SECURITY_DESCRIPTOR pSecurityDescriptor;
PACL pAcl;
PTOKEN_USER pTokenUser;
BYTE aclBuffer[ ACL_BUFFER_SIZE + 8 ];
BYTE tokenBuffer[ TOKEN_BUFFER_SIZE + 8 ];
} SECURITY_INFO;
/* Initialise an ACL allowing only the creator access and return it to the
caller as an opaque value */
CHECK_RETVAL_PTR \
void *initACLInfo( const int access )
{
SECURITY_INFO *securityInfo;
HANDLE hToken = INVALID_HANDLE_VALUE; /* See comment below */
BOOLEAN tokenOK = FALSE;
/* Win95/98/ME don't have any security, return null security info */
if( getSysVar( SYSVAR_ISWIN95 ) )
return( NULL );
/* Allocate and initialise the composite security info structure */
if( ( securityInfo = \
clAlloc( "initACLInfo", sizeof( SECURITY_INFO ) ) ) == NULL )
return( NULL );
memset( securityInfo, 0, sizeof( SECURITY_INFO ) );
securityInfo->pAcl = ( PACL ) securityInfo->aclBuffer;
securityInfo->pTokenUser = ( PTOKEN_USER ) securityInfo->tokenBuffer;
/* Get the security token for this thread. First we try for the thread
token (which it typically only has when impersonating), if we don't
get that we use the token associated with the process. We also
initialise the hToken (above) even though it shouldn't be necessary
because Windows tries to read its contents, which indicates there
might be problems if it happens to start out with the wrong value */
if( OpenThreadToken( GetCurrentThread(), TOKEN_QUERY, FALSE, &hToken ) || \
OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &hToken ) )
{
DWORD cbTokenUser;
tokenOK = GetTokenInformation( hToken, TokenUser,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -