From f7f376af57a08ee7d7e4a8872a2fcd3e3694549f Mon Sep 17 00:00:00 2001 From: neurolabusc Date: Fri, 9 Sep 2022 11:02:55 -0400 Subject: [PATCH] Detect some types of CSA corruption (https://github.com/rordenlab/dcm2niix/issues/633) --- console/nii_dicom.cpp | 26 +++++++++++++++++++++----- console/nii_dicom.h | 2 +- console/nii_dicom_batch.cpp | 12 ++++++++++-- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp index 8d589d20..0bed8410 100644 --- a/console/nii_dicom.cpp +++ b/console/nii_dicom.cpp @@ -1398,6 +1398,10 @@ int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, i // Storage order is always little-endian, so byte-swap required values if necessary if (!littleEndianPlatform()) nifti_swap_4bytes(1, &tagCSA.nitems); + if (tagCSA.nitems > 128) { + printError("%d n_tags CSA Image Header corrupted (0029,1010) see issue 633.\n", tagCSA.nitems); + return EXIT_FAILURE; + } if (isVerbose > 1) //extreme verbosity: show every CSA tag printMessage(" %d CSA of %s %d\n", lPos, tagCSA.name, tagCSA.nitems); if (tagCSA.nitems > 0) { @@ -5952,12 +5956,25 @@ const uint32_t kEffectiveTE = 0x0018 + (0x9082 << 16); char iceStr[kDICOMStr]; dcmStr(lLength, &buffer[lPos], iceStr); dcmStrDigitsOnly(iceStr); - char *end; - int echo = (int)strtol(iceStr, &end, 10); + char *end, *echoStr; + //read the first item ('X' or numeric if uncombined) + char c = iceStr[0]; + if( c >= '0' && c <= '9' ){ + int coilNumber = (int)strtol(iceStr, &end, 10); + //if ((iceStr != end) && (coilNumber > 0) && (strlen(d.coilName) < 1)) { //nb with uncombined coil will still have a name, e.g. 'HeadNeck_64' + if ((iceStr != end) && (coilNumber > 0)) { + sprintf(d.coilName, "%d", coilNumber); + //printf("issue631 coil name '%s'\n", d.coilName); + d.coilCrc = mz_crc32X((unsigned char *)&d.coilName, strlen(d.coilName)); + } + } + //read the second item: the echo number ('4') + echoStr = strchr(iceStr, ' '); + int echo = (int)strtol(echoStr, &end, 10); //printMessage("%d:%d:'%s'\n", d.echoNum, echo, iceStr); if (iceStr != end) d.echoNum = echo; - //printMessage("%d:'%s'\n", echo, iceStr); + //printMessage("%d:'%s'\n", echo, echoStr); break; } case kPhaseEncodingDirectionPositiveSiemens: { @@ -6730,7 +6747,7 @@ const uint32_t kEffectiveTE = 0x0018 + (0x9082 << 16); case kCSAImageHeaderInfo: if ((lPos + lLength) > fileLen) break; - readCSAImageHeader(&buffer[lPos], lLength, &d.CSA, isVerbose, d.is3DAcq); //, dti4D); + readCSAImageHeader(&buffer[lPos], lLength, &d.CSA, isVerbose, d.is3DAcq); if (!d.isHasPhase) d.isHasPhase = d.CSA.isPhaseMap; if ((d.CSA.coilNumber > 0) && (strlen(d.coilName) < 1)) { @@ -6738,7 +6755,6 @@ const uint32_t kEffectiveTE = 0x0018 + (0x9082 << 16); //printf("issue631 coil name '%s'\n", d.coilName); d.coilCrc = mz_crc32X((unsigned char *)&d.coilName, strlen(d.coilName)); } - //okra break; case kCSASeriesHeaderInfo: if ((lPos + lLength) > fileLen) diff --git a/console/nii_dicom.h b/console/nii_dicom.h index e6abc75c..72481efa 100644 --- a/console/nii_dicom.h +++ b/console/nii_dicom.h @@ -50,7 +50,7 @@ extern "C" { #define kCPUsuf " " //unknown CPU #endif -#define kDCMdate "v1.0.20220903" +#define kDCMdate "v1.0.20220909" #define kDCMvers kDCMdate " " kJP2suf kLSsuf kCCsuf kCPUsuf static const int kMaxEPI3D = 1024; //maximum number of EPI images in Siemens Mosaic diff --git a/console/nii_dicom_batch.cpp b/console/nii_dicom_batch.cpp index 5cfb3ad9..c1672871 100644 --- a/console/nii_dicom_batch.cpp +++ b/console/nii_dicom_batch.cpp @@ -629,6 +629,10 @@ int phoenixOffsetCSASeriesHeader(unsigned char *buff, int lLength) { memcpy(&tagCSA, &buff[lPos], sizeof(tagCSA)); //read tag if (!littleEndianPlatform()) nifti_swap_4bytes(1, &tagCSA.nitems); + if (tagCSA.nitems > 128) { + printError("%d n_tags CSA Series Header corrupted (0029,1020 ) see issue 633.\n", tagCSA.nitems); + return EXIT_FAILURE; + } //printf("%d CSA of %s %d\n",lPos, tagCSA.name, tagCSA.nitems); lPos += sizeof(tagCSA); if (strcmp(tagCSA.name, "MrPhoenixProtocol") == 0) @@ -707,8 +711,14 @@ void siemensCsaAscii(const char *filename, TCsaAscii *csaAscii, int csaOffset, i size_t result = fread(buffer, 1, csaLength, pFile); if ((int)result != csaLength) return; + fclose(pFile); + //next bit complicated: restrict to ASCII portion to avoid buffer overflow errors in BINARY portion int startAscii = phoenixOffsetCSASeriesHeader((unsigned char *)buffer, csaLength); + if (startAscii == EXIT_FAILURE) { + free(buffer); + return; + } int csaLengthTrim = csaLength; char *bufferTrim = buffer; if ((startAscii > 0) && (startAscii < csaLengthTrim)) { //ignore binary data at start @@ -889,7 +899,6 @@ void siemensCsaAscii(const char *filename, TCsaAscii *csaAscii, int csaOffset, i char keyStrSh7[] = "sGRADSPEC.alShimCurrent[4]"; shimSetting[7] = readKeyFloat(keyStrSh7, keyPos, csaLengthTrim); } - fclose(pFile); free(buffer); return; } // siemensCsaAscii() @@ -1332,7 +1341,6 @@ tse3d: T2*/ if ((strstr(d.imageTypeText, "_ND") != NULL) || (strstr(d.imageType, "_ND") != NULL) || (strstr(d.imageTypeText, "_ND") != NULL) || (strstr(d.imageType, "_ND") != NULL)) fprintf(fp, "\t\"NonlinearGradientCorrection\": false,\n"); - if (d.isDerived) //DICOM is derived image or non-spatial file (sounds, etc) fprintf(fp, "\t\"RawImage\": false,\n"); if (d.seriesNum > 0)