diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index c2b1afa49..4e0755db7 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -51,8 +51,11 @@ static char *pNullString = ""; #define messagesize 276 #define RPCL_LEN INFIX_TO_POSTFIX_SIZE(80) -/* must be long enough to hold 32-bit signed integer in base 10 */ -STATIC_ASSERT(messagesize>=11); +/* Must be big enough to hold a 64-bit integer in base 10, but in + * the future when fields hold large JSON objects this fixed size + * allocation will probably have to become variable sized. + */ +STATIC_ASSERT(messagesize >= 21); static char *ppstring[5]={" NPP"," PP"," CA"," CP"," CPP"}; static char *msstring[4]={" NMS"," MS"," MSI"," MSS"}; @@ -208,11 +211,13 @@ static void zeroDbentry(DBENTRY *pdbentry) static char *getpMessage(DBENTRY *pdbentry) { char *msg = pdbentry->message; + if (!msg) { msg = dbCalloc(1, messagesize); pdbentry->message = msg; } - *msg = '\0'; + else + *msg = '\0'; return msg; } @@ -224,6 +229,17 @@ void dbMsgCpy(DBENTRY *pdbentry, const char *msg) pdbentry->message[messagesize-1] = '\0'; } +static +void dbMsgNCpy(DBENTRY *pdbentry, const char *msg, size_t len) +{ + getpMessage(pdbentry); + if (len >= messagesize) + len = messagesize-1; /* FIXME: Quietly truncates */ + + strncpy(pdbentry->message, msg, len); + pdbentry->message[len] = '\0'; +} + static void dbMsgPrint(DBENTRY *pdbentry, const char *fmt, ...) { @@ -1678,21 +1694,27 @@ int dbIsVisibleRecord(DBENTRY *pdbentry) long dbCreateAlias(DBENTRY *pdbentry, const char *alias) { - dbRecordType *precordType = pdbentry->precordType; - dbRecordNode *precnode = pdbentry->precnode; - dbRecordNode *pnewnode; - PVDENTRY *ppvd; - ELLLIST *preclist = NULL; - if (!precordType) return S_dbLib_recordTypeNotFound; + dbRecordType *precordType = pdbentry->precordType; + dbRecordNode *precnode = pdbentry->precnode; + dbRecordNode *pnewnode; + DBENTRY tempEntry; + PVDENTRY *ppvd; + + if (!precordType) + return S_dbLib_recordTypeNotFound; + /* alias of alias still references actual record */ - while(precnode && (precnode->flags&DBRN_FLAGS_ISALIAS)) + while (precnode && (precnode->flags & DBRN_FLAGS_ISALIAS)) precnode = precnode->aliasedRecnode; - if (!precnode) return S_dbLib_recNotFound; - zeroDbentry(pdbentry); - if (!dbFindRecord(pdbentry, alias)) return S_dbLib_recExists; - zeroDbentry(pdbentry); - pdbentry->precordType = precordType; - preclist = &precordType->recList; + + if (!precnode) + return S_dbLib_recNotFound; + + dbInitEntry(pdbentry->pdbbase, &tempEntry); + if (!dbFindRecord(&tempEntry, alias)) + return S_dbLib_recExists; + dbFinishEntry(&tempEntry); + pnewnode = dbCalloc(1, sizeof(dbRecordNode)); pnewnode->recordname = epicsStrDup(alias); pnewnode->precord = precnode->precord; @@ -1700,11 +1722,16 @@ long dbCreateAlias(DBENTRY *pdbentry, const char *alias) pnewnode->flags = DBRN_FLAGS_ISALIAS; precnode->flags |= DBRN_FLAGS_HASALIAS; ellInit(&pnewnode->infoList); - ellAdd(preclist, &pnewnode->node); + + ellAdd(&precordType->recList, &pnewnode->node); precordType->no_aliases++; - pdbentry->precnode = pnewnode; + ppvd = dbPvdAdd(pdbentry->pdbbase, precordType, pnewnode); - if (!ppvd) {errMessage(-1,"Logic Err: Could not add to PVD");return(-1);} + if (!ppvd) { + errMessage(-1, "dbCreateAlias: Add to PVD failed"); + return -1; + } + return 0; } @@ -1888,7 +1915,8 @@ char * dbGetString(DBENTRY *pdbentry) switch (pflddes->field_type) { case DBF_STRING: - dbMsgCpy(pdbentry, (char *)pfield); + /* Protect against a missing nil-terminator */ + dbMsgNCpy(pdbentry, (char *)pfield, pflddes->size); break; case DBF_CHAR: case DBF_UCHAR: @@ -1911,6 +1939,8 @@ char * dbGetString(DBENTRY *pdbentry) case CONSTANT: if (plink->value.constantStr) { dbMsgCpy(pdbentry, plink->value.constantStr); + } else if (plink->text) { + dbMsgCpy(pdbentry, plink->text); } else { dbMsgCpy(pdbentry, ""); } @@ -2011,7 +2041,13 @@ char * dbGetString(DBENTRY *pdbentry) switch(plink->type) { case CONSTANT: - dbMsgCpy(pdbentry, "0"); + if (plink->value.constantStr) { + dbMsgCpy(pdbentry, plink->value.constantStr); + } else if (plink->text) { + dbMsgCpy(pdbentry, plink->text); + } else { + dbMsgCpy(pdbentry, ""); + } break; case MACRO_LINK: if (plink->value.macro_link.macroStr) { diff --git a/src/std/rec/stringinRecord.c b/src/std/rec/stringinRecord.c index 212e79378..ee84f0dc9 100644 --- a/src/std/rec/stringinRecord.c +++ b/src/std/rec/stringinRecord.c @@ -96,6 +96,7 @@ static long init_record(struct dbCommon *pcommon, int pass) { struct stringinRecord *prec = (struct stringinRecord *)pcommon; STATIC_ASSERT(sizeof(prec->oval)==sizeof(prec->val)); + STATIC_ASSERT(sizeof(prec->sval)==sizeof(prec->val)); struct stringindset *pdset = (struct stringindset *) prec->dset; if (pass == 0) return 0; @@ -120,7 +121,8 @@ static long init_record(struct dbCommon *pcommon, int pass) if (status) return status; } - strcpy(prec->oval, prec->val); + + strncpy(prec->oval, prec->val, sizeof(prec->val)); return 0; } @@ -213,7 +215,7 @@ static long readValue(stringinRecord *prec) if (prec->pact || (prec->sdly < 0.)) { status = dbGetLink(&prec->siol, DBR_STRING, prec->sval, 0, 0); if (status == 0) { - strcpy(prec->val, prec->sval); + strncpy(prec->val, prec->sval, sizeof(prec->val)); prec->udf = FALSE; } prec->pact = FALSE; diff --git a/src/std/rec/stringoutRecord.c b/src/std/rec/stringoutRecord.c index fedc9fc68..0c871fc4d 100644 --- a/src/std/rec/stringoutRecord.c +++ b/src/std/rec/stringoutRecord.c @@ -98,6 +98,7 @@ static long init_record(struct dbCommon *pcommon, int pass) { struct stringoutRecord *prec = (struct stringoutRecord *)pcommon; STATIC_ASSERT(sizeof(prec->oval)==sizeof(prec->val)); + STATIC_ASSERT(sizeof(prec->ivov)==sizeof(prec->val)); struct stringoutdset *pdset = (struct stringoutdset *) prec->dset; if (pass == 0) return 0; @@ -126,7 +127,7 @@ static long init_record(struct dbCommon *pcommon, int pass) return status; } - strcpy(prec->oval, prec->val); + strncpy(prec->oval, prec->val, sizeof(prec->val)); return 0; } @@ -165,7 +166,7 @@ static long process(struct dbCommon *pcommon) break; case (menuIvoaSet_output_to_IVOV) : if(prec->pact == FALSE){ - strcpy(prec->val,prec->ivov); + strncpy(prec->val, prec->ivov, sizeof(prec->val)); } status=writeValue(prec); /* write the new value */ break; diff --git a/test/ioc/db/testdbConvert.c b/test/ioc/db/testdbConvert.c index cd0ed0ed2..cc175c0a0 100644 --- a/test/ioc/db/testdbConvert.c +++ b/test/ioc/db/testdbConvert.c @@ -38,7 +38,7 @@ static void testBasicGet(void) getter(&addr, scratch, 1, s_input_len, 0); - testOk1(scratch[0]==s_input[0]); + testOk1(scratch[0]==s_input[0] && scratch[1]==0); memset(scratch, 0x42, sizeof(s_input)); } @@ -128,7 +128,7 @@ static void testBasicPut(void) putter(&addr, s_input, 1, s_input_len, 0); - testOk1(scratch[0]==s_input[0]); + testOk1(scratch[0]==s_input[0] && scratch[1]==0); memset(scratch, 0x42, sizeof(s_input)); } diff --git a/test/std/rec/asTestLib.c b/test/std/rec/asTestLib.c index 18139233f..2043ed770 100644 --- a/test/std/rec/asTestLib.c +++ b/test/std/rec/asTestLib.c @@ -109,11 +109,7 @@ static void hookPass0(initHookState state) else testFail("Wrong link type: %d", (int)prec->out.type); - /* note that dbGetString() reads an empty string before links are initialized - * should probably be considered a bug, but has been the case for so long - * we call it a 'feature'. - */ - checkGetString(&entry, ""); + checkGetString(&entry, "rec0.DISV"); testOk1(dbPutString(&entry, "rec0.SEVR")==0); } else{ diff --git a/test/std/rec/softTest.c b/test/std/rec/softTest.c index b29c955f9..e9cb319a0 100644 --- a/test/std/rec/softTest.c +++ b/test/std/rec/softTest.c @@ -12,6 +12,7 @@ #include "dbTest.h" #include "dbUnitTest.h" #include "epicsThread.h" +#include "epicsEvent.h" #include "errlog.h" #include "registryFunction.h" #include "subRecord.h" @@ -134,24 +135,25 @@ void testGroup2(void) int dest; +epicsEventId destEvent; static long destSubr(subRecord *prec) { dest = prec->val; prec->val = -1; + epicsEventMustTrigger(destEvent); return 0; } static -void checkOutput(const char *rec, int value) +void checkOutput3(const char *rec, int value) { testDiag("Checking record '%s'", rec); testdbPutFieldOk(rec, DBR_LONG, value); - /* Even with a local CA link, the dest record gets processed in - * the context of this thread (i.e. immediately). TPRO confirms. - */ + + epicsEventMustWait(destEvent); testOk(dest == value, "value %d output -> %d", value, dest); } @@ -170,22 +172,33 @@ void testGroup3(void) NULL, }; + destEvent = epicsEventMustCreate(epicsEventEmpty); + testDiag("============ Starting %s ============", EPICS_FUNCTION); for (rec = records; *rec; rec++) { - checkOutput(*rec, 1); + checkOutput3(*rec, 1); checkDtyp(*rec); } - checkOutput("do3.B0", 1); + checkOutput3("do3.B0", 1); checkDtyp("do3"); - checkOutput("do3c.B0", 1); + checkOutput3("do3c.B0", 1); checkDtyp("do3c"); for (rec = records; *rec; rec++) { - checkOutput(*rec, 0); + checkOutput3(*rec, 0); } - checkOutput("do3.B0", 0); - checkOutput("do3c.B0", 0); + checkOutput3("do3.B0", 0); + checkOutput3("do3c.B0", 0); +} + + +static +void checkOutput4(const char *rec, int value) +{ + testDiag("Checking record '%s'", rec); + + testdbPutFieldOk(rec, DBR_LONG, value); } /* Group 4 are all soft-channel output records with OUT being empty @@ -202,7 +215,7 @@ void testGroup4(void) testDiag("============ Starting %s ============", EPICS_FUNCTION); for (rec = records; *rec; rec++) { - checkOutput(*rec, 0); + checkOutput4(*rec, 0); } } @@ -211,7 +224,7 @@ void recTestIoc_registerRecordDeviceDriver(struct dbBase *); MAIN(softTest) { - testPlan(266); + testPlan(258); testdbPrepare(); testdbReadDatabase("recTestIoc.dbd", NULL, NULL);