diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html
index 51ef2eeb1..fc46ff8cb 100644
--- a/documentation/RELEASE_NOTES.html
+++ b/documentation/RELEASE_NOTES.html
@@ -72,6 +72,20 @@ dbQuietMacroWarnings=1 VxWorks
Changes pulled from the 3.14 branch since 3.15.3
+String field buffer overflows
+
+Two buffer overflow bugs that can crash the IOC have been fixed, caused by
+initializing a string field with a value larger than the field size
+(Launchpad bug
+#1563191).
+
+Fixed stack corruption bug in epicsThread C++ API
+
+The C++ interface to the epicsThread API could corrupt the stack on thread
+exit in some rare circumstances, usually at program exit. This bug has been
+fixed (Launchpad bug
+#1558206).
+
RTEMS NTP Support Issue
On RTEMS the NTP Time Provider could in some circumstances get out of sync
diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c
index d9b02b670..6f933f217 100644
--- a/src/ioc/dbStatic/dbStaticLib.c
+++ b/src/ioc/dbStatic/dbStaticLib.c
@@ -2051,7 +2051,10 @@ long dbPutString(DBENTRY *pdbentry,const char *pstring)
switch (pflddes->field_type) {
case DBF_STRING:
if(!pfield) return(S_dbLib_fieldNotFound);
- strncpy((char *)pfield, pstring,pflddes->size);
+ if(strlen(pstring) >= (size_t)pflddes->size) return S_dbLib_strLen;
+ strncpy((char *)pfield, pstring, pflddes->size-1);
+ ((char *)pfield)[pflddes->size-1] = 0;
+
if((pflddes->special == SPC_CALC) && !stringHasMacro) {
char rpcl[RPCL_LEN];
short err;
@@ -2062,7 +2065,6 @@ long dbPutString(DBENTRY *pdbentry,const char *pstring)
calcErrorStr(err), pstring);
}
}
- if((short)strlen(pstring) >= pflddes->size) status = S_dbLib_strLen;
break;
case DBF_CHAR:
diff --git a/src/libCom/osi/epicsThread.cpp b/src/libCom/osi/epicsThread.cpp
index 60b017b30..f59c0bd1a 100644
--- a/src/libCom/osi/epicsThread.cpp
+++ b/src/libCom/osi/epicsThread.cpp
@@ -4,7 +4,7 @@
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
-* in file LICENSE that is included with this distribution.
+* in file LICENSE that is included with this distribution.
\*************************************************************************/
//
// $Revision-Id$
@@ -37,22 +37,22 @@ epicsThreadRunable::~epicsThreadRunable () {}
void epicsThreadRunable::run () {}
void epicsThreadRunable::show ( unsigned int ) const {}
-class epicsThread :: unableToCreateThread :
+class epicsThread :: unableToCreateThread :
public std :: exception {
public:
const char * what () const throw ();
};
-const char * epicsThread ::
+const char * epicsThread ::
unableToCreateThread :: what () const throw ()
{
return "unable to create thread";
}
-void epicsThread :: printLastChanceExceptionMessage (
+void epicsThread :: printLastChanceExceptionMessage (
const char * pExceptionTypeName,
const char * pExceptionContext )
-{
+{
char date[64];
try {
epicsTime cur = epicsTime :: getCurrent ();
@@ -63,51 +63,52 @@ void epicsThread :: printLastChanceExceptionMessage (
}
char name [128];
epicsThreadGetName ( this->id, name, sizeof ( name ) );
- errlogPrintf (
+ errlogPrintf (
"epicsThread: Unexpected C++ exception \"%s\" "
"with type \"%s\" in thread \"%s\" at %s\n",
pExceptionContext, pExceptionTypeName, name, date );
errlogFlush ();
- // this should behave as the C++ implementation intends when an
- // exception isnt handled. If users dont like this behavior, they
- // can install an application specific unexpected handler.
+ // This behavior matches the C++ implementation when an exception
+ // isn't handled by the thread code. Users can install their own
+ // application-specific unexpected handler if preferred.
std::unexpected ();
}
extern "C" void epicsThreadCallEntryPoint ( void * pPvt )
{
- epicsThread * pThread =
+ epicsThread * pThread =
static_cast ( pPvt );
- bool waitRelease = false;
+ bool threadDestroyed = false;
try {
- pThread->pWaitReleaseFlag = & waitRelease;
+ pThread->pThreadDestroyed = & threadDestroyed;
if ( pThread->beginWait () ) {
pThread->runable.run ();
- // current thread may have run the destructor
- // so must not touch the this pointer from
- // here on down if waitRelease is true
+ // The run() routine may have destroyed the epicsThread
+ // object by now; pThread can only be used below here
+ // when the threadDestroyed flag is false.
}
}
catch ( const epicsThread::exitException & ) {
}
catch ( std :: exception & except ) {
- if ( ! waitRelease ) {
- pThread->printLastChanceExceptionMessage (
+ if ( ! threadDestroyed ) {
+ pThread->printLastChanceExceptionMessage (
typeid ( except ).name (), except.what () );
}
}
catch ( ... ) {
- if ( ! waitRelease ) {
- pThread->printLastChanceExceptionMessage (
+ if ( ! threadDestroyed ) {
+ pThread->printLastChanceExceptionMessage (
"catch ( ... )", "Non-standard C++ exception" );
}
}
- if ( ! waitRelease ) {
+ if ( ! threadDestroyed ) {
epicsGuard < epicsMutex > guard ( pThread->mutex );
+ pThread->pThreadDestroyed = NULL;
pThread->terminated = true;
pThread->exitEvent.signal ();
- // once the terminated flag is set and we release the lock
- // then the "this" pointer must not be touched again
+ // After the terminated flag is set and guard's destructor
+ // releases the lock, pThread must never be used again.
}
}
@@ -135,12 +136,12 @@ void epicsThread::exitWait () throw ()
bool epicsThread::exitWait ( const double delay ) throw ()
{
try {
- // if destructor is running in managed thread then of
- // course we will not wait for the managed thread to
- // exit
+ // When called (usually by a destructor) in the context of
+ // the managed thread we can't wait for the thread to exit.
+ // Set the threadDestroyed flag and return success.
if ( this->isCurrentThread() ) {
- if ( this->pWaitReleaseFlag ) {
- *this->pWaitReleaseFlag = true;
+ if ( this->pThreadDestroyed ) {
+ *this->pThreadDestroyed = true;
}
return true;
}
@@ -157,14 +158,14 @@ bool epicsThread::exitWait ( const double delay ) throw ()
}
}
catch ( std :: exception & except ) {
- errlogPrintf (
+ errlogPrintf (
"epicsThread::exitWait(): Unexpected exception "
- " \"%s\"\n",
+ " \"%s\"\n",
except.what () );
epicsThreadSleep ( epicsMin ( delay, 5.0 ) );
}
catch ( ... ) {
- errlogPrintf (
+ errlogPrintf (
"Non-standard unexpected exception in "
"epicsThread::exitWait()\n" );
epicsThreadSleep ( epicsMin ( delay, 5.0 ) );
@@ -174,14 +175,14 @@ bool epicsThread::exitWait ( const double delay ) throw ()
return this->terminated;
}
-epicsThread::epicsThread (
+epicsThread::epicsThread (
epicsThreadRunable & runableIn, const char * pName,
unsigned stackSize, unsigned priority ) :
- runable ( runableIn ), id ( 0 ), pWaitReleaseFlag ( 0 ),
+ runable ( runableIn ), id ( 0 ), pThreadDestroyed ( 0 ),
begin ( false ), cancel ( false ), terminated ( false )
{
- this->id = epicsThreadCreate (
- pName, priority, stackSize, epicsThreadCallEntryPoint,
+ this->id = epicsThreadCreate (
+ pName, priority, stackSize, epicsThreadCallEntryPoint,
static_cast < void * > ( this ) );
if ( ! this->id ) {
throw unableToCreateThread ();
@@ -193,11 +194,11 @@ epicsThread::~epicsThread () throw ()
while ( ! this->exitWait ( 10.0 ) ) {
char nameBuf [256];
this->getName ( nameBuf, sizeof ( nameBuf ) );
- fprintf ( stderr,
+ fprintf ( stderr,
"epicsThread::~epicsThread(): "
- "blocking for thread \"%s\" to exit\n",
+ "blocking for thread \"%s\" to exit\n",
nameBuf );
- fprintf ( stderr,
+ fprintf ( stderr,
"was epicsThread object destroyed before thread exit ?\n");
}
}
@@ -272,11 +273,6 @@ void epicsThread::sleep (double seconds) throw ()
epicsThreadSleep (seconds);
}
-//epicsThread & epicsThread::getSelf ()
-//{
-// return * static_cast ( epicsThreadGetIdSelf () );
-//}
-
const char *epicsThread::getNameSelf () throw ()
{
return epicsThreadGetNameSelf ();
@@ -303,7 +299,7 @@ void epicsThread :: show ( unsigned level ) const throw ()
if ( level > 0u ) {
epicsThreadShow ( this->id, level - 1 );
if ( level > 1u ) {
- ::printf ( "pWaitReleaseFlag = %p\n", this->pWaitReleaseFlag );
+ ::printf ( "pThreadDestroyed = %p\n", this->pThreadDestroyed );
::printf ( "begin = %c, cancel = %c, terminated = %c\n",
this->begin ? 'T' : 'F',
this->cancel ? 'T' : 'F',
@@ -323,12 +319,12 @@ extern "C" {
epicsThreadPrivateId okToBlockPrivate;
static const int okToBlockNo = 0;
static const int okToBlockYes = 1;
-
+
static void epicsThreadOnceIdInit(void *)
{
okToBlockPrivate = epicsThreadPrivateCreate();
}
-
+
int epicsShareAPI epicsThreadIsOkToBlock(void)
{
const int *pokToBlock;
@@ -336,7 +332,7 @@ extern "C" {
pokToBlock = (int *) epicsThreadPrivateGet(okToBlockPrivate);
return (pokToBlock ? *pokToBlock : 0);
}
-
+
void epicsShareAPI epicsThreadSetOkToBlock(int isOkToBlock)
{
const int *pokToBlock;
@@ -344,12 +340,12 @@ extern "C" {
pokToBlock = (isOkToBlock) ? &okToBlockYes : &okToBlockNo;
epicsThreadPrivateSet(okToBlockPrivate, (void *)pokToBlock);
}
-
+
epicsThreadId epicsShareAPI epicsThreadMustCreate (
const char *name, unsigned int priority, unsigned int stackSize,
- EPICSTHREADFUNC funptr,void *parm)
+ EPICSTHREADFUNC funptr,void *parm)
{
- epicsThreadId id = epicsThreadCreate (
+ epicsThreadId id = epicsThreadCreate (
name, priority, stackSize, funptr, parm );
assert ( id );
return id;
diff --git a/src/libCom/osi/epicsThread.h b/src/libCom/osi/epicsThread.h
index 141044661..84b2c4788 100644
--- a/src/libCom/osi/epicsThread.h
+++ b/src/libCom/osi/epicsThread.h
@@ -158,10 +158,10 @@ public:
bool isCurrentThread () const throw ();
bool operator == ( const epicsThread & ) const throw ();
void show ( unsigned level ) const throw ();
+
/* these operate on the current thread */
static void suspendSelf () throw ();
static void sleep (double seconds) throw ();
- /* static epicsThread & getSelf (); */
static const char * getNameSelf () throw ();
static bool isOkToBlock () throw ();
static void setOkToBlock ( bool isOkToBlock ) throw ();
@@ -174,7 +174,7 @@ private:
epicsMutex mutex;
epicsEvent event;
epicsEvent exitEvent;
- bool * pWaitReleaseFlag;
+ bool * pThreadDestroyed;
bool begin;
bool cancel;
bool terminated;