dbCaPutLinkCallback can write out of bounds

Bug #1423635 reported by mdavidsaver
6
This bug affects 1 person
Affects Status Importance Assigned to Milestone
EPICS Base
Fix Released
Medium
mdavidsaver

Bug Description

When dbCaPutLinkCallback() writes arrays using the requested size without considering the destination size (pca->nelements).

> record(aao, "a") {
> field(OUT, "b CA")
> field(FTVL, "DOUBLE")
> field(NELM, "10")
> }
>
> record(aai, "b") {
> field(FTVL, "DOUBLE")
> field(NELM, "2")
> }

When 'a' is written with number of elements greater than 2.

> $ caput -a a 10 1 2 3 4 5 6 7 8 9 10

Valgrind reports:

> Invalid write of size 8
> at 0x569107A: putDoubleDouble (in /usr/lib/x86_64-linux-gnu/libdbIoc.so.3.14.12.3)
> by 0x56A103E: dbCaPutLinkCallback (in /usr/lib/x86_64-linux-gnu/libdbIoc.so.3.14.12.3)
> by 0x568C709: dbPutLinkValue (in /usr/lib/x86_64-linux-gnu/libdbIoc.so.3.14.12.3)
> by 0x505C2F1: write_aao (in /usr/lib/x86_64-linux-gnu/libsoftDevIoc.so.3.14.12.3)
> by 0x4E344D7: process (in /usr/lib/x86_64-linux-gnu/librecIoc.so.3.14.12.3)
> by 0x568B73A: dbProcess (in /usr/lib/x86_64-linux-gnu/libdbIoc.so.3.14.12.3)
> by 0x568C4E1: dbPutField (in /usr/lib/x86_64-linux-gnu/libdbIoc.so.3.14.12.3)
> by 0x569D452: db_put_field (in /usr/lib/x86_64-linux-gnu/libdbIoc.so.3.14.12.3)
> by 0x6553284: write_action (in /usr/lib/x86_64-linux-gnu/librsrvIoc.so.3.14.12.3)
> by 0x65519DE: camessage (in /usr/lib/x86_64-linux-gnu/librsrvIoc.so.3.14.12.3)
> by 0x654E73A: camsgtask (in /usr/lib/x86_64-linux-gnu/librsrvIoc.so.3.14.12.3)
> by 0x5D180E6: start_routine (in /usr/lib/x86_64-linux-gnu/libCom.so.3.14.12.3)
> Address 0x7d5d390 is 0 bytes after a block of size 16 alloc'd
> at 0x4C272B8: calloc (vg_replace_malloc.c:566)
> by 0x5D0F12C: callocMustSucceed (in /usr/lib/x86_64-linux-gnu/libCom.so.3.14.12.3)
> by 0x56A1223: dbCaPutLinkCallback (in /usr/lib/x86_64-linux-gnu/libdbIoc.so.3.14.12.3)
> by 0x568C709: dbPutLinkValue (in /usr/lib/x86_64-linux-gnu/libdbIoc.so.3.14.12.3)
> by 0x505C2F1: write_aao (in /usr/lib/x86_64-linux-gnu/libsoftDevIoc.so.3.14.12.3)
> by 0x4E344D7: process (in /usr/lib/x86_64-linux-gnu/librecIoc.so.3.14.12.3)
> by 0x568B73A: dbProcess (in /usr/lib/x86_64-linux-gnu/libdbIoc.so.3.14.12.3)
> by 0x568C4E1: dbPutField (in /usr/lib/x86_64-linux-gnu/libdbIoc.so.3.14.12.3)
> by 0x569D452: db_put_field (in /usr/lib/x86_64-linux-gnu/libdbIoc.so.3.14.12.3)
> by 0x6553284: write_action (in /usr/lib/x86_64-linux-gnu/librsrvIoc.so.3.14.12.3)
> by 0x65519DE: camessage (in /usr/lib/x86_64-linux-gnu/librsrvIoc.so.3.14.12.3)
> by 0x654E73A: camsgtask (in /usr/lib/x86_64-linux-gnu/librsrvIoc.so.3.14.12.3)

Related branches

Changed in epics-base:
assignee: nobody → mdavidsaver (mdavidsaver)
importance: Undecided → Medium
milestone: none → 3.14.branch
Revision history for this message
Andrew Johnson (anj) wrote :

Interesting, I'm slightly surprised that caget didn't catch that.

Putting up to twice the length of the target is still safe because the circular buffer code makes the pdest pointer wrap around after it reaches the end of the buffer the first time, but after the second time it will overflow.

Should this have been checked/prevented by the dbConvert.c routines? That file changed significantly in 3.15, so I would say don't bother doing it in 3.14 in any case.

Revision history for this message
Andrew Johnson (anj) wrote :

Cancel that surprise, your caput is legal.

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

> Should this have been checked/prevented by the dbConvert.c routines?

This is the question. How do we want dbPutConvertRoutine[][] to behave? Either it does the check, or it doesn't.

I would note that the only other caller of dbPutConvertRoutine[][] in Base now is dbPut(), which does an explicit bounds check before calling.

> That file changed significantly in 3.15, so I would say don't bother doing it in 3.14 in any case.

For 3.14 the easy fix of adding a bounds check in dbCaPutLinkCallback() is IMO worthwhile.

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

I found this while writing unittests for dbCa while I will be posting soon.

Revision history for this message
Andrew Johnson (anj) wrote :

> For 3.14 the easy fix of adding a bounds check in dbCaPutLinkCallback() is IMO worthwhile.

Agreed, please go ahead with that.

BTW one of the tests we should have for dbCa (which will fail so we also have to fix the code) is to have a link pointing to a channel that changes data type and/or array size over a disconnect/reconnect. This is bug#541221, which I was looking at and trying to fix just the other day. I have code that unsubscribes the native data type when a channel reconnects with changed parameters, but so far I have only succeeded in moving the assertion failure to a different spot. I hope to resolve this soon now that I've started on it, but I have other things I have to work on too which might delay the fix.

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

Of course the opposite case has the opposite problem. Writing 2 elements into 10 leaves 8 uninitialized.

I guess the short term (3.14/3.15) fix is to zero the remaining buffer? For 3.16 should this be changed to only ca_array_put() with the actual number of elements?

> Invalid read of size 4
> at 0x402734: checkArray (dbCaLinkTest.c:300)
> by 0x402ABA: testArrayLink (dbCaLinkTest.c:372)
> by 0x402B3F: main (dbCaLinkTest.c:394)
> Address 0x6e25288 is 0 bytes after a block of size 40 alloc'd
> at 0x4C272B8: calloc (vg_replace_malloc.c:566)
> by 0x539F1F9: callocMustSucceed (cantProceed.c:28)
> by 0x40425F: init_record (arrRecord.c:85)

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

Fix committed to the 3.14 branch which prevents the buffer overflow when the destination is too small, and zeros uninitialized elements when the destination is too large. The written array size does not change.

Changed in epics-base:
status: New → Fix Committed
Andrew Johnson (anj)
Changed in epics-base:
status: Fix Committed → Fix Released
To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.