From 159055ea888c0ecbfce9a95fcea92a5d8b180b2a Mon Sep 17 00:00:00 2001 From: Sergio Pascual Date: Mar 04 2019 13:24:25 +0000 Subject: Backport security fixes from 3.440 (rhbz #1570484) --- diff --git a/cfitsio-backport344.patch b/cfitsio-backport344.patch new file mode 100644 index 0000000..b98cdc4 --- /dev/null +++ b/cfitsio-backport344.patch @@ -0,0 +1,2747 @@ +diff -ur cfitsio3430/cfileio.c cfitsio3440/cfileio.c +--- cfitsio3430/cfileio.c 2018-03-01 17:28:51.000000000 +0100 ++++ cfitsio3440/cfileio.c 2018-04-11 16:33:41.000000000 +0200 +@@ -1490,7 +1490,7 @@ + char oldbinspec[FLEN_FILENAME], oldcolspec[FLEN_FILENAME]; + char cwd[FLEN_FILENAME]; + char tmpStr[FLEN_FILENAME]; +- char tmpinfile[FLEN_FILENAME]; ++ char tmpinfile[FLEN_FILENAME]; + + *isopen = 0; + +@@ -1508,7 +1508,8 @@ + + if(fits_strcasecmp(urltype,"FILE://") == 0) + { +- fits_path2url(infile,tmpinfile,status); ++ if (fits_path2url(infile,FLEN_FILENAME,tmpinfile,status)) ++ return (*status); + + if(tmpinfile[0] != '/') + { +@@ -1546,7 +1547,8 @@ + + if(fits_strcasecmp(oldurltype,"FILE://") == 0) + { +- fits_path2url(oldinfile,tmpStr,status); ++ if(fits_path2url(oldinfile,FLEN_FILENAME,tmpStr,status)) ++ return(*status); + + if(tmpStr[0] != '/') + { +@@ -1856,7 +1858,7 @@ + fitsfile *newptr; + int ii, hdunum, slen, colnum = -1, testnum, deletecol = 0, savecol = 0; + int numcols = 0, *colindex = 0, tstatus = 0; +- char *cptr, *cptr2, *cptr3, *clause = NULL, keyname[FLEN_KEYWORD]; ++ char *tstbuff=0, *cptr, *cptr2, *cptr3, *clause = NULL, keyname[FLEN_KEYWORD]; + char colname[FLEN_VALUE], oldname[FLEN_VALUE], colformat[FLEN_VALUE]; + char *file_expr = NULL, testname[FLEN_VALUE], card[FLEN_CARD]; + +@@ -1962,11 +1964,15 @@ + + if (clause[0] == '!' || clause[0] == '-') + { ++ char *clause1 = clause+1; + /* ===================================== */ + /* Case I. delete this column or keyword */ + /* ===================================== */ + +- if (ffgcno(*fptr, CASEINSEN, &clause[1], &colnum, status) <= 0) ++ /* Check that clause does not have leading '#' and ++ that column name exists */ ++ if (clause1[0] && clause1[0] != '#' && ++ ffgcno(*fptr, CASEINSEN, clause1, &colnum, status) <= 0) + { + /* a column with this name exists, so try to delete it */ + if (ffdcol(*fptr, colnum, status) > 0) +@@ -1984,18 +1990,41 @@ + } + else + { ++ int delall = 0, clen = 0; + ffcmsg(); /* clear previous error message from ffgcno */ + /* try deleting a keyword with this name */ + *status = 0; +- if (ffdkey(*fptr, &clause[1], status) > 0) +- { +- ffpmsg("column or keyword to be deleted does not exist:"); +- ffpmsg(clause); +- if( colindex ) free( colindex ); +- if( file_expr ) free( file_expr ); +- if( clause ) free(clause); +- return(*status); +- } ++ /* skip past leading '#' if any */ ++ if (clause1[0] == '#') clause1++; ++ clen = strlen(clause1); ++ ++ /* Repeat deletion of keyword if requested with trailing '+' */ ++ if (clen > 1 && clause1[clen-1] == '+') { ++ delall = 1; ++ clause1[clen-1] = 0; ++ } ++ /* Single or repeated deletions until done */ ++ do { ++ if (ffdkey(*fptr, clause1, status) > 0) ++ { ++ if (delall && *status == KEY_NO_EXIST && ++ (strchr(clause1,'*') || strchr(clause1,'?')) ) { ++ /* Found last wildcard item. Stop deleting */ ++ ffcmsg(); ++ *status = 0; ++ delall = 0; /* Force end of this loop */ ++ } else { ++ /* This was not a wildcard deletion, or it resulted in ++ another kind of error */ ++ ffpmsg("column or keyword to be deleted does not exist:"); ++ ffpmsg(clause1); ++ if( colindex ) free( colindex ); ++ if( file_expr ) free( file_expr ); ++ if( clause ) free(clause); ++ return(*status); ++ } ++ } ++ } while(delall); /* end do{} */ + } + } + else +@@ -2011,17 +2040,32 @@ + calculation expression (case 2B) */ + /* ===================================================== */ + cptr2 = clause; +- slen = fits_get_token(&cptr2, "( =", colname, NULL); ++ slen = fits_get_token2(&cptr2, "( =", &tstbuff, NULL, status); + +- if (slen == 0) ++ if (slen == 0 || *status) + { +- ffpmsg("error: column or keyword name is blank:"); ++ ffpmsg("error: column or keyword name is blank (ffedit_columns):"); + ffpmsg(clause); + if( colindex ) free( colindex ); + if( file_expr ) free( file_expr ); + if (clause) free(clause); ++ if (*status==0) ++ *status=URL_PARSE_ERROR; ++ return(*status); ++ } ++ if (strlen(tstbuff) > FLEN_VALUE-1) ++ { ++ ffpmsg("error: column or keyword name is too long (ffedit_columns):"); ++ ffpmsg(clause); ++ if( colindex ) free( colindex ); ++ if( file_expr ) free( file_expr ); ++ if (clause) free(clause); ++ free(tstbuff); + return(*status= URL_PARSE_ERROR); + } ++ strcpy(colname, tstbuff); ++ free(tstbuff); ++ tstbuff=0; + + /* If this is a keyword of the form + #KEYWORD# +@@ -2091,9 +2135,22 @@ + */ + if (*cptr2 == '(') + { +- fits_get_token(&cptr2, ")", oldname, NULL); +- strcat(colname, oldname); ++ fits_get_token2(&cptr2, ")", &tstbuff, NULL, status); ++ if (*status || (strlen(tstbuff) + strlen(colname) + 1) > ++ FLEN_VALUE-1) ++ { ++ ffpmsg("error: column name is too long (ffedit_columns):"); ++ if( file_expr ) free( file_expr ); ++ if (clause) free(clause); ++ free(tstbuff); ++ if (*status==0) ++ *status=URL_PARSE_ERROR; ++ return (*status); ++ } ++ strcat(colname, tstbuff); + strcat(colname, ")"); ++ free(tstbuff); ++ tstbuff=0; + cptr2++; + } + +@@ -2177,7 +2234,20 @@ + while (*cptr2 == ' ') + cptr2++; /* skip white space */ + +- fits_get_token(&cptr2, " ", oldname, NULL); ++ fits_get_token2(&cptr2, " ", &tstbuff, NULL, status); ++ if (*status || strlen(tstbuff) > FLEN_VALUE-1) ++ { ++ ffpmsg("error: column name syntax is too long (ffedit_columns):"); ++ if( file_expr ) free( file_expr ); ++ if (clause) free(clause); ++ free(tstbuff); ++ if (*status==0) ++ *status=URL_PARSE_ERROR; ++ return (*status); ++ } ++ strcpy(oldname, tstbuff); ++ free(tstbuff); ++ tstbuff=0; + + /* get column number of the existing column */ + if (ffgcno(*fptr, CASEINSEN, oldname, &colnum, status) <= 0) +@@ -2232,12 +2302,40 @@ + colformat[0] = '\0'; + cptr3 = colname; + +- fits_get_token(&cptr3, "(", oldname, NULL); ++ fits_get_token2(&cptr3, "(", &tstbuff, NULL, status); ++ if (*status || strlen(tstbuff) > FLEN_VALUE-1) ++ { ++ ffpmsg("column expression is too long (ffedit_columns)"); ++ if( colindex ) free( colindex ); ++ if( file_expr ) free( file_expr ); ++ if (clause) free(clause); ++ free(tstbuff); ++ if (*status==0) ++ *status=URL_PARSE_ERROR; ++ return(*status); ++ } ++ strcpy(oldname, tstbuff); ++ free(tstbuff); ++ tstbuff=0; + + if (cptr3[0] == '(' ) + { + cptr3++; /* skip the '(' */ +- fits_get_token(&cptr3, ")", colformat, NULL); ++ fits_get_token2(&cptr3, ")", &tstbuff, NULL, status); ++ if (*status || strlen(tstbuff) > FLEN_VALUE-1) ++ { ++ ffpmsg("column expression is too long (ffedit_columns)"); ++ if( colindex ) free( colindex ); ++ if( file_expr ) free( file_expr ); ++ if (clause) free(clause); ++ free(tstbuff); ++ if (*status==0) ++ *status=URL_PARSE_ERROR; ++ return(*status); ++ } ++ strcpy(colformat, tstbuff); ++ free(tstbuff); ++ tstbuff=0; + } + + /* calculate values for the column or keyword */ +@@ -3282,13 +3380,24 @@ + */ + { + int slen, isanumber; +- char token[FLEN_VALUE]; ++ char token[FLEN_VALUE], *tstbuff=0; + + if (*status > 0) + return(*status); + +- slen = fits_get_token(ptr, " ,:", token, &isanumber); /* get 1st token */ +- ++ slen = fits_get_token2(ptr, " ,:", &tstbuff, &isanumber, status); /* get 1st token */ ++ if (*status || strlen(tstbuff) > FLEN_VALUE-1) ++ { ++ ffpmsg("Error: image section string too long (fits_get_section_range)"); ++ free(tstbuff); ++ if (*status==0) ++ *status = URL_PARSE_ERROR; ++ return(*status); ++ } ++ strcpy(token, tstbuff); ++ free(tstbuff); ++ tstbuff=0; ++ + /* support [:2,:2] type syntax, where the leading * is implied */ + if (slen==0) strcpy(token,"*"); + +@@ -3311,8 +3420,18 @@ + *secmin = atol(token); + + (*ptr)++; /* skip the colon between the min and max values */ +- slen = fits_get_token(ptr, " ,:", token, &isanumber); /* get token */ +- ++ slen = fits_get_token2(ptr, " ,:", &tstbuff, &isanumber, status); /* get token */ ++ if (*status || strlen(tstbuff) > FLEN_VALUE-1) ++ { ++ ffpmsg("Error: image section string too long (fits_get_section_range)"); ++ free(tstbuff); ++ if (*status==0) ++ *status = URL_PARSE_ERROR; ++ return(*status); ++ } ++ strcpy(token, tstbuff); ++ free(tstbuff); ++ tstbuff=0; + if (slen == 0 || !isanumber) + return(*status = URL_PARSE_ERROR); + +@@ -3323,7 +3442,18 @@ + if (**ptr == ':') + { + (*ptr)++; /* skip the colon between the max and incre values */ +- slen = fits_get_token(ptr, " ,", token, &isanumber); /* get token */ ++ slen = fits_get_token2(ptr, " ,", &tstbuff, &isanumber, status); /* get token */ ++ if (*status || strlen(tstbuff) > FLEN_VALUE-1) ++ { ++ ffpmsg("Error: image section string too long (fits_get_section_range)"); ++ free(tstbuff); ++ if (*status==0) ++ *status = URL_PARSE_ERROR; ++ return(*status); ++ } ++ strcpy(token, tstbuff); ++ free(tstbuff); ++ tstbuff=0; + + if (slen == 0 || !isanumber) + return(*status = URL_PARSE_ERROR); +@@ -5063,9 +5193,14 @@ + /* to the output file, and is not the urltype of the input file */ + ptr2 = 0; /* so reset pointer to zero */ + } +- ++ + if (ptr2) /* copy the explicit urltype string */ + { ++ if (ptr2-ptr1+3 >= MAX_PREFIX_LEN) ++ { ++ ffpmsg("Name of urltype is too long."); ++ return(*status = URL_PARSE_ERROR); ++ } + if (urltype) + strncat(urltype, ptr1, ptr2 - ptr1 + 3); + ptr1 = ptr2 + 3; +diff -ur cfitsio3430/drvrfile.c cfitsio3440/drvrfile.c +--- cfitsio3430/drvrfile.c 2018-03-01 17:28:51.000000000 +0100 ++++ cfitsio3440/drvrfile.c 2018-04-11 16:33:41.000000000 +0200 +@@ -358,7 +358,7 @@ + /* Get the current working directory */ + fits_get_cwd(cwd, &status); + slen = strlen(cwd); +- if (cwd[slen-1] != '/') strcat(cwd,"/"); /* make sure the CWD ends with slash */ ++ if ((slen < FLEN_FILENAME) && cwd[slen-1] != '/') strcat(cwd,"/"); /* make sure the CWD ends with slash */ + + + /* check that CWD string matches the rootstring */ +@@ -370,6 +370,7 @@ + + /* get the user name from CWD (it follows the root string) */ + strncpy(username, cwd+rootlen, 50); /* limit length of user name */ ++ username[50]=0; + cpos=strchr(username, '/'); + if (!cpos) { + ffpmsg("invalid CWD: not equal to root data directory + username"); +@@ -765,7 +766,7 @@ + /* Open file. Try various suffix combinations */ + if (file_openfile(filename, 0, &diskfile)) + { +- if (strlen(filename) > FLEN_FILENAME - 1) ++ if (strlen(filename) > FLEN_FILENAME - 5) + return(0); + + strcpy(tmpfilename,filename); +@@ -773,7 +774,7 @@ + if (file_openfile(filename, 0, &diskfile)) + { + #if HAVE_BZIP2 +- strcpy(tmpfilename,filename); ++ strcpy(filename,tmpfilename); + strcat(filename,".bz2"); + if (file_openfile(filename, 0, &diskfile)) + { +diff -ur cfitsio3430/drvrgsiftp.c cfitsio3440/drvrgsiftp.c +--- cfitsio3430/drvrgsiftp.c 2018-03-01 17:28:51.000000000 +0100 ++++ cfitsio3440/drvrgsiftp.c 2018-04-11 16:33:41.000000000 +0200 +@@ -2,7 +2,7 @@ + /* This file, drvrgsiftp.c contains driver routines for gsiftp files. */ + /* Andrea Barisani */ + /* Taffoni Giuliano */ +-#ifdef HAVE_NET_SERVICES ++#ifdef HAVE_NET_SERVICES + #ifdef HAVE_GSIFTP + + #include +@@ -21,6 +21,7 @@ + + static int gsiftpopen = 0; + static int global_offset = 0; ++static int free_gsiftp_tmp=0; + static int gsiftp_get(char *filename, FILE **gsiftpfile, int num_streams); + + static globus_mutex_t lock; +@@ -45,7 +46,9 @@ + ffpmsg("Cannot create temporary directory!"); + return (FILE_NOT_OPENED); + } +- gsiftp_tmpfile = malloc(strlen(gsiftp_tmpdir) + strlen("/gsiftp_buffer.tmp")); ++ gsiftp_tmpfile = malloc(strlen(gsiftp_tmpdir) + strlen("/gsiftp_buffer.tmp")+1); ++ gsiftp_tmpfile[0]=0; ++ free_gsiftp_tmp=1; + strcat(gsiftp_tmpfile, gsiftp_tmpdir); + strcat(gsiftp_tmpfile, "/gsiftp_buffer.tmp"); + } +@@ -56,8 +59,8 @@ + int gsiftp_shutdown(void) + { + free(gsiftpurl); +- free(gsiftp_tmpfile); +- free(gsiftp_tmpdir); ++ if (free_gsiftp_tmp) ++ free(gsiftp_tmpfile); + + return file_shutdown(); + } +@@ -356,6 +359,11 @@ + done = GLOBUS_FALSE; + + strcpy(gsiurl,"gsiftp://"); ++ if (strlen(gsiurl)+strlen(filename) > MAXLEN-1) ++ { ++ ffpmsg("file name too long (gsiftp_get)"); ++ return (FILE_NOT_OPENED); ++ } + strcat(gsiurl,filename); + + *gsiftpfile = fopen(gsiftp_tmpfile,"w+"); +@@ -448,6 +456,11 @@ + done = GLOBUS_FALSE; + + strcpy(gsiurl,"gsiftp://"); ++ if (strlen(gsiurl)+strlen(filename) > MAXLEN-1) ++ { ++ ffpmsg("file name too long (gsiftp_put)"); ++ return (FILE_NOT_OPENED); ++ } + strcat(gsiurl,filename); + + *gsiftpfile = fopen(gsiftp_tmpfile,"r"); +diff -ur cfitsio3430/drvrnet.c cfitsio3440/drvrnet.c +--- cfitsio3430/drvrnet.c 2018-03-01 17:28:51.000000000 +0100 ++++ cfitsio3440/drvrnet.c 2018-04-11 16:33:41.000000000 +0200 +@@ -920,6 +920,12 @@ + /* return the new URL string, and set contentencoding to "ftp" as + a flag to the http_checkfile routine + */ ++ if (strlen(scratchstr2) > FLEN_FILENAME-1) ++ { ++ ffpmsg("Error: redirected url string too long (http_open_network)"); ++ fclose(*httpfile); ++ return URL_PARSE_ERROR; ++ } + strcpy(url, scratchstr2); + strcpy(contentencoding,"ftp://"); + fclose (*httpfile); +@@ -935,6 +941,12 @@ + /* return the new URL string, and set contentencoding to "https" as + a flag to the http_checkfile routine + */ ++ if (strlen(scratchstr2) > FLEN_FILENAME-1) ++ { ++ ffpmsg("Error: redirected url string too long (http_open_network)"); ++ fclose(*httpfile); ++ return URL_PARSE_ERROR; ++ } + strcpy(url, scratchstr2); + strcpy(contentencoding,"https://"); + fclose(*httpfile); +@@ -973,6 +985,12 @@ + /* Found the : */ + scratchstr++; /* skip the : */ + scratchstr++; /* skip the extra space */ ++ if (strlen(scratchstr) > SHORTLEN-1) ++ { ++ ffpmsg("Error: content-encoding string too long (http_open_network)"); ++ fclose(*httpfile); ++ return URL_PARSE_ERROR; ++ } + strcpy(contentencoding,scratchstr); + } + } +@@ -2556,6 +2574,11 @@ + if ((thost = strchr(urlcopy, '@')) != NULL) + urlcopy = thost+1; + ++ if (strlen(urlcopy) > SHORTLEN-1) ++ { ++ free(urlcopyorig); ++ return 1; ++ } + strcpy(host,urlcopy); + thost = host; + while (*urlcopy != '/' && *urlcopy != ':' && *urlcopy) { +@@ -2572,6 +2595,11 @@ + } + } else { + /* do this for ftp */ ++ if (strlen(urlcopy) > SHORTLEN-1) ++ { ++ free(urlcopyorig); ++ return 1; ++ } + strcpy(host,urlcopy); + thost = host; + while (*urlcopy != '/' && *urlcopy) { +@@ -2585,6 +2613,11 @@ + /* Now the rest is a fn */ + + if (*urlcopy) { ++ if (strlen(urlcopy) > MAXLEN-1) ++ { ++ free(urlcopyorig); ++ return 1; ++ } + strcpy(fn,urlcopy); + } + free(urlcopyorig); +@@ -2645,7 +2678,11 @@ + if (!strstr(infile,".gz") && (!strstr(infile,".Z"))) { + /* The infile string does not contain the name of a compressed file. */ + /* Fisrt, look for a .gz compressed version of the file. */ +- ++ ++ if (strlen(infile) + 3 > MAXLEN-1) ++ { ++ return URL_PARSE_ERROR; ++ } + strcpy(newinfile,infile); + strcat(newinfile,".gz"); + +@@ -2700,7 +2737,11 @@ + + if (!foundfile) { + /* did not find .gz compressed version of the file, so look for .Z file. */ +- ++ ++ if (strlen(infile+2) > MAXLEN-1) ++ { ++ return URL_PARSE_ERROR; ++ } + strcpy(newinfile,infile); + strcat(newinfile,".Z"); + if (!http_open_network(newinfile,&httpfile,contentencoding, +@@ -2878,6 +2919,10 @@ + /* The infile string does not contain the name of a compressed file. */ + /* Fisrt, look for a .gz compressed version of the file. */ + ++ if (strlen(infile)+3 > MAXLEN-1) ++ { ++ return URL_PARSE_ERROR; ++ } + strcpy(newinfile,infile); + strcat(newinfile,".gz"); + +@@ -2888,6 +2933,10 @@ + } + + if (!foundfile) { ++ if (strlen(infile)+2 > MAXLEN-1) ++ { ++ return URL_PARSE_ERROR; ++ } + strcpy(newinfile,infile); + strcat(newinfile,".Z"); + +@@ -3312,6 +3361,11 @@ + + + /* Parse the URL apart again */ ++ if (strlen(url)+7 > MAXLEN-1) ++ { ++ ffpmsg("Error: url too long"); ++ return(FILE_NOT_OPENED); ++ } + strcpy(turl,"root://"); + strcat(turl,url); + if (NET_ParseUrl(turl,proto,host,&port,fn)) { +@@ -3329,6 +3383,11 @@ + + /* get the username */ + if (NULL != getenv("ROOTUSERNAME")) { ++ if (strlen(getenv("ROOTUSERNAME")) > MAXLEN-1) ++ { ++ ffpmsg("root user name too long (root_openfile)"); ++ return (FILE_NOT_OPENED); ++ } + strcpy(recbuf,getenv("ROOTUSERNAME")); + } else { + printf("Username: "); +@@ -3357,6 +3416,11 @@ + + /* now the password */ + if (NULL != getenv("ROOTPASSWORD")) { ++ if (strlen(getenv("ROOTPASSWORD")) > MAXLEN-1) ++ { ++ ffpmsg("root password too long (root_openfile)"); ++ return (FILE_NOT_OPENED); ++ } + strcpy(recbuf,getenv("ROOTPASSWORD")); + } else { + printf("Password: "); +@@ -3387,6 +3451,11 @@ + } + + /* now the file open request */ ++ if (strlen(fn)+strlen(rwmode)+1 > MAXLEN-1) ++ { ++ ffpmsg("root file name too long (root_openfile)"); ++ return (FILE_NOT_OPENED); ++ } + strcpy(recbuf,fn); + strcat(recbuf," "); + strcat(recbuf,rwmode); +diff -ur cfitsio3430/editcol.c cfitsio3440/editcol.c +--- cfitsio3430/editcol.c 2018-03-01 17:28:51.000000000 +0100 ++++ cfitsio3440/editcol.c 2018-04-11 16:33:41.000000000 +0200 +@@ -1097,6 +1097,11 @@ + delbyte = 0; + for (ii = 0; ii < ncols; ii++) + { ++ if (strlen(tform[ii]) > FLEN_VALUE-1) ++ { ++ ffpmsg("Column format string too long (fficls)"); ++ return (*status=BAD_TFORM); ++ } + strcpy(tfm, tform[ii]); + ffupch(tfm); /* make sure format is in upper case */ + +diff -ur cfitsio3430/eval_f.c cfitsio3440/eval_f.c +--- cfitsio3430/eval_f.c 2018-03-01 17:28:51.000000000 +0100 ++++ cfitsio3440/eval_f.c 2018-04-11 16:33:41.000000000 +0200 +@@ -593,7 +593,7 @@ + if( parInfo==NULL || *parInfo=='\0' ) { + /* Figure out best default column type */ + if( gParse.hdutype==BINARY_TBL ) { +- snprintf(tform,16,"%ld",nelem); ++ snprintf(tform,15,"%ld",nelem); + switch( Info.datatype ) { + case TLOGICAL: strcat(tform,"L"); break; + case TLONG: strcat(tform,"J"); break; +diff -ur cfitsio3430/fitscore.c cfitsio3440/fitscore.c +--- cfitsio3430/fitscore.c 2018-03-01 17:28:51.000000000 +0100 ++++ cfitsio3440/fitscore.c 2018-04-11 16:33:41.000000000 +0200 +@@ -976,21 +977,21 @@ + (int) (ii+1), (int) card[ii] ); + + if (card[ii] == 0) +- strcat(msg, " (NULL char.)"); ++ strncat(msg, " (NULL char.)",FLEN_ERRMSG-strlen(msg)-1); + else if (card[ii] == 9) +- strcat(msg, " (TAB char.)"); ++ strncat(msg, " (TAB char.)",FLEN_ERRMSG-strlen(msg)-1); + else if (card[ii] == 10) +- strcat(msg, " (Line Feed char.)"); ++ strncat(msg, " (Line Feed char.)",FLEN_ERRMSG-strlen(msg)-1); + else if (card[ii] == 11) +- strcat(msg, " (Vertical Tab)"); ++ strncat(msg, " (Vertical Tab)",FLEN_ERRMSG-strlen(msg)-1); + else if (card[ii] == 12) +- strcat(msg, " (Form Feed char.)"); ++ strncat(msg, " (Form Feed char.)",FLEN_ERRMSG-strlen(msg)-1); + else if (card[ii] == 13) +- strcat(msg, " (Carriage Return)"); ++ strncat(msg, " (Carriage Return)",FLEN_ERRMSG-strlen(msg)-1); + else if (card[ii] == 27) +- strcat(msg, " (Escape char.)"); ++ strncat(msg, " (Escape char.)",FLEN_ERRMSG-strlen(msg)-1); + else if (card[ii] == 127) +- strcat(msg, " (Delete char.)"); ++ strncat(msg, " (Delete char.)",FLEN_ERRMSG-strlen(msg)-1); + + ffpmsg(msg); + +@@ -1118,6 +1119,13 @@ + /* for now at least, treat all cases as an implicit ESO HIERARCH keyword. */ + /* This could change if FITS is ever expanded to directly support longer keywords. */ + ++ if (namelen + 11 > FLEN_CARD-1) ++ { ++ ffpmsg( ++ "The following keyword is too long to fit on a card:"); ++ ffpmsg(keyname); ++ return(*status = BAD_KEYCHAR); ++ } + strcat(card, "HIERARCH "); + strcat(card, tmpname); + namelen += 9; +@@ -1293,7 +1301,6 @@ + /* + Construct a keyword name string by appending the index number to the root. + e.g., if root = "TTYPE" and value = 12 then keyname = "TTYPE12". +- Note: this allows keyword names longer than 8 characters. + */ + { + char suffix[16]; +@@ -1312,7 +1319,9 @@ + rootlen--; /* remove trailing spaces in root name */ + keyname[rootlen] = '\0'; + } +- ++ if (strlen(suffix) + strlen(keyname) > 8) ++ return (*status=206); ++ + strcat(keyname, suffix); /* append suffix to the root */ + return(*status); + } +@@ -1582,7 +1591,7 @@ + { + char keyname[FLEN_KEYWORD], value[140], comment[140]; + char *tok, *suffix, *loc, tvalue[140]; +- int len, vlen, more, tstatus; ++ int len, vlen, more, tstatus, lentok1=0, remainlen=0; + double dval; + + if (*status > 0) +@@ -1619,36 +1628,41 @@ + tok++; + len = strspn(tok, " "); /* no. of spaces before keyword */ + tok += len; +- if (len < 8) /* not a blank name? */ +- { +- len = strcspn(tok, " ="); /* length of name */ +- if (len >= FLEN_KEYWORD) +- return(*status = BAD_KEYCHAR); ++ ++ len = strcspn(tok, " =+"); /* length of name */ ++ if (len >= FLEN_KEYWORD) ++ return(*status = BAD_KEYCHAR); + +- strncat(card, tok, len); ++ lentok1 = len; ++ strncat(card, tok, len); + +- /* +- The HIERARCH convention supports non-standard characters +- in the keyword name, so don't always convert to upper case or +- abort if there are illegal characters in the name or if the +- name is greater than 8 characters long. +- */ ++ /* ++ The HIERARCH convention supports non-standard characters ++ in the keyword name, so don't always convert to upper case or ++ abort if there are illegal characters in the name or if the ++ name is greater than 8 characters long. ++ */ + +- if (len < 9) /* this is possibly a normal FITS keyword name */ ++ if (len < 9) /* this is possibly a normal FITS keyword name */ ++ { ++ ffupch(card); ++ tstatus = 0; ++ if (fftkey(card, &tstatus) > 0) + { +- ffupch(card); +- tstatus = 0; +- if (fftkey(card, &tstatus) > 0) +- { +- /* name contained non-standard characters, so reset */ +- card[0] = '\0'; +- strncat(card, tok, len); +- } ++ /* name contained non-standard characters, so reset */ ++ card[0] = '\0'; ++ strncat(card, tok, len); + } +- +- tok += len; + } + ++ tok += len; ++ ++ /* Check optional "+" indicator to delete multiple keywords */ ++ if (tok[0] == '+' && len < FLEN_KEYWORD) { ++ strcat(card, "+"); ++ return (*status); ++ } ++ + /* second token, if present, is the new name for the keyword */ + + len = strspn(tok, " "); /* no. of spaces before next token */ +@@ -1659,13 +1673,24 @@ + + *hdtype = -2; + len = strcspn(tok, " "); /* length of new name */ +- if (len > 40) /* name has to fit on columns 41-80 of card */ +- return(*status = BAD_KEYCHAR); ++ /* this name has to fit on columns 41-80 of card, ++ and first name must now fit in 1-40 */ ++ if (lentok1 > 40) ++ { ++ card[0] = '\0'; ++ return (*status = BAD_KEYCHAR); ++ } ++ if (len > 40) ++ { ++ card[0] = '\0'; ++ return(*status = BAD_KEYCHAR); ++ } + + /* copy the new name to card + 40; This is awkward, */ + /* but is consistent with the way the Fortran FITSIO works */ + strcat(card," "); +- strncpy(&card[40], tok, len+1); /* copy len+1 to get terminator */ ++ strncpy(&card[40], tok, len); ++ card[80] = '\0'; /* necessary to add terminator in case len = 40 */ + + /* + The HIERARCH convention supports non-standard characters +@@ -1728,7 +1753,7 @@ + { + *hdtype = 1; /* simply append COMMENT and HISTORY keywords */ + strcpy(card, keyname); +- strncat(card, tok, 73); ++ strncat(card, tok, 72); + return(*status); + } + +@@ -1739,12 +1764,16 @@ + if (*tok == '\'') /* is value enclosed in quotes? */ + { + more = TRUE; ++ remainlen = 139; + while (more) + { + tok++; /* temporarily move past the quote char */ + len = strcspn(tok, "'"); /* length of quoted string */ + tok--; +- strncat(value, tok, len + 2); ++ if (len+2 > remainlen) ++ return (*status=BAD_KEYCHAR); ++ strncat(value, tok, len + 2); ++ remainlen -= (len+2); + + tok += len + 1; + if (tok[0] != '\'') /* check there is a closing quote */ +@@ -1762,7 +1791,8 @@ + else /* not a quoted string value */ + { + len = strcspn(tok, " /"); /* length of value string */ +- ++ if (len > 139) ++ return (*status=BAD_KEYCHAR); + strncat(value, tok, len); + if (!( (tok[0] == 'T' || tok[0] == 'F') && + (tok[1] == ' ' || tok[1] == '/' || tok[1] == '\0') )) +@@ -1796,6 +1826,8 @@ + if (*suffix != '\0' && *suffix != ' ' && *suffix != '/') + { + /* value is not a number; must enclose it in quotes */ ++ if (len > 137) ++ return (*status=BAD_KEYCHAR); + strcpy(value, "'"); + strncat(value, tok, len); + strcat(value, "'"); +@@ -2607,6 +2639,11 @@ + while (tform[ii] != 0 && tform[ii] == ' ') /* find first non-blank char */ + ii++; + ++ if (strlen(&tform[ii]) > FLEN_VALUE-1) ++ { ++ ffpmsg("Error: ASCII table TFORM code is too long (ffasfm)"); ++ return(*status = BAD_TFORM); ++ } + strcpy(temp, &tform[ii]); /* copy format string */ + ffupch(temp); /* make sure it is in upper case */ + form = temp; /* point to start of format string */ +@@ -2764,6 +2801,11 @@ + return(*status = BAD_TFORM); + } + ++ if (nchar-ii > FLEN_VALUE-1) ++ { ++ ffpmsg("Error: binary table TFORM code is too long (ffbnfm)."); ++ return (*status = BAD_TFORM); ++ } + strcpy(temp, &tform[ii]); /* copy format string */ + ffupch(temp); /* make sure it is in upper case */ + form = temp; /* point to start of format string */ +@@ -2779,7 +2821,13 @@ + if (ii == 0) + repeat = 1; /* no explicit repeat count */ + else +- sscanf(form,"%ld", &repeat); /* read repeat count */ ++ { ++ if (sscanf(form,"%ld", &repeat) != 1) /* read repeat count */ ++ { ++ ffpmsg("Error: Bad repeat format in TFORM (ffbnfm)."); ++ return(*status = BAD_TFORM); ++ } ++ } + + /*-----------------------------------------------*/ + /* determine datatype code */ +@@ -2948,7 +2996,12 @@ + ffpmsg("Error: binary table TFORM code is blank (ffbnfmll)."); + return(*status = BAD_TFORM); + } +- ++ ++ if (strlen(&tform[ii]) > FLEN_VALUE-1) ++ { ++ ffpmsg("Error: binary table TFORM code is too long (ffbnfmll)."); ++ return(*status = BAD_TFORM); ++ } + strcpy(temp, &tform[ii]); /* copy format string */ + ffupch(temp); /* make sure it is in upper case */ + form = temp; /* point to start of format string */ +@@ -6559,7 +6612,7 @@ + 'len' is the maximum length of the vector in the table (e.g., '1PE(400)') + */ + { +- int ii; ++ int ii, lenform=0; + long tflds; + LONGLONG length, addr, maxlen, naxis2, jj; + char comment[FLEN_COMMENT], keyname[FLEN_KEYWORD]; +@@ -6598,13 +6651,20 @@ + /* construct the new keyword value */ + strcpy(newform, "'"); + tmp = strchr(tform, '('); /* truncate old length, if present */ +- if (tmp) *tmp = 0; +- strcat(newform, tform); ++ if (tmp) *tmp = 0; ++ lenform = strlen(tform); + + /* print as double, because the string-to-64-bit */ + /* conversion is platform dependent (%lld, %ld, %I64d) */ + + snprintf(lenval,40, "(%.0f)", (double) maxlen); ++ ++ if (lenform+strlen(lenval)+2 > FLEN_VALUE-1) ++ { ++ ffpmsg("Error assembling TFORMn string (ffuptf)."); ++ return(*status = BAD_TFORM); ++ } ++ strcat(newform, tform); + + strcat(newform,lenval); + while(strlen(newform) < 9) +@@ -9234,7 +9294,7 @@ + if (errno == ERANGE) + { + strcpy(msg,"Range Error in ffc2jj converting string to longlong int: "); +- strncat(msg,cval,25); ++ strncat(msg,cval,23); + ffpmsg(msg); + + *status = NUM_OVERFLOW; +@@ -9349,6 +9409,12 @@ + + if (strchr(cval, 'D') || decimalpt == ',') { + /* strtod expects a comma, not a period, as the decimal point */ ++ if (strlen(cval) > 72) ++ { ++ strcpy(msg,"Error: Invalid string to float in ffc2rr"); ++ ffpmsg(msg); ++ return (*status=BAD_C2F); ++ } + strcpy(tval, cval); + + /* The C language does not support a 'D'; replace with 'E' */ +@@ -9419,6 +9485,12 @@ + + if (strchr(cval, 'D') || decimalpt == ',') { + /* need to modify a temporary copy of the string before parsing it */ ++ if (strlen(cval) > 72) ++ { ++ strcpy(msg,"Error: Invalid string to double in ffc2dd"); ++ ffpmsg(msg); ++ return (*status=BAD_C2D); ++ } + strcpy(tval, cval); + /* The C language does not support a 'D'; replace with 'E' */ + if ((loc = strchr(tval, 'D'))) *loc = 'E'; +diff -ur cfitsio3430/fpackutil.c cfitsio3440/fpackutil.c +--- cfitsio3430/fpackutil.c 2018-03-01 17:28:51.000000000 +0100 ++++ cfitsio3440/fpackutil.c 2018-04-11 23:05:04.000000000 +0200 +@@ -63,16 +63,23 @@ + int status = 0, hdunum; + char msg[SZ_STR]; + +- fits_file_name(infptr, tempfilename, &status); +- fits_get_hdu_num(infptr, &hdunum); +- +- fits_close_file (infptr, &status); +- +- snprintf(msg, SZ_STR,"Error processing file: %s\n", tempfilename); +- fp_msg (msg); +- snprintf(msg, SZ_STR," in HDU number %d\n", hdunum); +- fp_msg (msg); +- ++ if (infptr) ++ { ++ fits_file_name(infptr, tempfilename, &status); ++ fits_get_hdu_num(infptr, &hdunum); ++ ++ fits_close_file (infptr, &status); ++ ++ snprintf(msg, SZ_STR,"Error processing file: %s\n", tempfilename); ++ fp_msg (msg); ++ snprintf(msg, SZ_STR," in HDU number %d\n", hdunum); ++ fp_msg (msg); ++ } ++ else ++ { ++ snprintf(msg, SZ_STR,"Error: Unable to process input file\n"); ++ fp_msg(msg); ++ } + fits_report_error (stderr, stat); + + if (outfptr) { +@@ -129,6 +136,13 @@ + + for (ii = 0; ii < maxtry; ii++) { + if (fp_access(tmpnam)) break; /* good, the file does not exist */ ++ if (strlen(tmpnam) > SZ_STR-2) ++ { ++ fp_msg ("\nCould not create temporary file name:\n"); ++ fp_msg (tmpnam); ++ fp_msg ("\n"); ++ exit (-1); ++ } + strcat(tmpnam, "x"); /* append an x to the name, and try again */ + } + +@@ -203,7 +217,8 @@ + } + + for (iarg=fpvar.firstfile; iarg < argc; iarg++) { +- strncpy (infits, argv[iarg], SZ_STR); ++ strncpy (infits, argv[iarg], SZ_STR-1); ++ infits[SZ_STR-1]=0; + + if (strchr (infits, '[') || strchr (infits, ']')) { + fp_msg ("Error: section/extension notation not supported: "); +@@ -472,6 +487,11 @@ + + /* if gzipping the output, make sure .gz file doesn't exist */ + if (fpptr->do_gzip_file) { ++ if (strlen(outfits)+3 > SZ_STR-1) ++ { ++ fp_msg ("Error: output file name too long:\n "); fp_msg (outfits); ++ fp_msg ("\n "); fp_noop (); exit (-1); ++ } + strcat(outfits, ".gz"); + if (fp_access (outfits) == 0) { + fp_msg ("Error: output file already exists:\n "); fp_msg (outfits); +@@ -486,6 +506,11 @@ + /* check that input file exists */ + if (infits[0] != '-') { /* if not reading from stdin stream */ + if (fp_access (infits) != 0) { /* if not, then check if */ ++ if (strlen(infits)+3 > SZ_STR-1) ++ { ++ fp_msg ("Error: input file name too long:\n "); fp_msg (infits); ++ fp_msg ("\n "); fp_noop (); exit (-1); ++ } + strcat(infits, ".gz"); /* a gzipped version exsits */ + if (fp_access (infits) != 0) { + namelen = strlen(infits); +@@ -553,7 +578,8 @@ + { + char infits[SZ_STR], outfits[SZ_STR]; + char temp[SZ_STR], answer[30]; +- int iarg, islossless, namelen, iraf_infile = 0, status = 0, ifail; ++ char valchar[]="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.#()+,-_@[]/^{}"; ++ int ichar=0, outlen=0, iarg, islossless, namelen, iraf_infile = 0, status = 0, ifail; + + if (fpvar.initialized != FP_INIT_MAGIC) { + fp_msg ("Error: internal initialization error\n"); exit (-1); +@@ -598,6 +624,7 @@ + islossless = 1; + + strncpy (infits, argv[iarg], SZ_STR - 1); ++ infits[SZ_STR-1]=0; + + if (unpack) { + /* ********** This section applies to funpack ************ */ +@@ -678,6 +705,7 @@ + } + + strncpy(temp, outfits, SZ_STR-1); ++ temp[SZ_STR-1]=0; + + if (infits[0] != '-') { /* if not reading from stdin stream */ + if (!strcmp(infits, outfits) ) { /* are input and output names the same? */ +@@ -828,6 +856,20 @@ + + if (fpvar.do_gzip_file) { /* gzip the output file */ + strcpy(temp, "gzip -1 "); ++ outlen = strlen(outfits); ++ if (outlen + 8 > SZ_STR-1) ++ { ++ fp_msg("\nError: Output file name is too long.\n"); ++ exit(-1); ++ } ++ for (ichar=0; ichar < outlen; ++ichar) ++ { ++ if (!strchr(valchar, outfits[ichar])) ++ { ++ fp_msg("\n Error: Invalid characters in output file name.\n"); ++ exit(-1); ++ } ++ } + strcat(temp,outfits); + system(temp); + strcat(outfits, ".gz"); /* only possibible with funpack */ +@@ -1197,11 +1239,12 @@ + snprintf(dimen,100," (%ld", naxes[0]); + len =strlen(dimen); + for (ii = 1; ii < naxis; ii++) { +- if (len < 100) ++ if (len < 99) + snprintf(dimen+len,100-len,",%ld", naxes[ii]); + len =strlen(dimen); + } +- strcat(dimen, ")"); ++ if (strlen(dimen)<99) ++ strcat(dimen, ")"); + printf("%-12s",dimen); + + fits_get_hduaddr(inputfptr, &headstart, &datastart, &dataend, &stat); +diff -ur cfitsio3430/getcols.c cfitsio3440/getcols.c +--- cfitsio3430/getcols.c 2018-03-01 17:28:51.000000000 +0100 ++++ cfitsio3440/getcols.c 2018-04-11 16:33:41.000000000 +0200 +@@ -296,8 +296,9 @@ + + /* write the formated string for each value */ + if (nulval) { +- strcpy(tmpnull, nulval); +- nulwidth = strlen(nulval); ++ strncpy(tmpnull, nulval,79); ++ tmpnull[79]='\0'; /* In case len(nulval) >= 79 */ ++ nulwidth = strlen(tmpnull); + } else { + strcpy(tmpnull, " "); + nulwidth = 1; +@@ -430,8 +431,9 @@ + } + + if (nulval) { +- strcpy(tmpnull, nulval); +- nulwidth = strlen(nulval); ++ strncpy(tmpnull, nulval,79); ++ tmpnull[79]='\0'; ++ nulwidth = strlen(tmpnull); + } else { + strcpy(tmpnull, " "); + nulwidth = 1; +diff -ur cfitsio3430/getkey.c cfitsio3440/getkey.c +--- cfitsio3430/getkey.c 2018-03-01 17:28:51.000000000 +0100 ++++ cfitsio3440/getkey.c 2018-04-11 16:33:42.000000000 +0200 +@@ -1451,6 +1451,10 @@ + equalssign = strchr(card, '='); + if (equalssign == 0) continue; /* keyword has no value */ + ++ if (equalssign - card - lenroot > 7) ++ { ++ return (*status=BAD_KEYCHAR); ++ } + strncat(keyindex, &card[lenroot], equalssign - card - lenroot); /* copy suffix */ + tstatus = 0; + if (ffc2ii(keyindex, &ival, &tstatus) <= 0) /* test suffix */ +@@ -1528,6 +1532,10 @@ + equalssign = strchr(card, '='); + if (equalssign == 0) continue; /* keyword has no value */ + ++ if (equalssign - card - lenroot > 7) ++ { ++ return (*status=BAD_KEYCHAR); ++ } + strncat(keyindex, &card[lenroot], equalssign - card - lenroot); /* copy suffix */ + + tstatus = 0; +@@ -1605,6 +1613,10 @@ + equalssign = strchr(card, '='); + if (equalssign == 0) continue; /* keyword has no value */ + ++ if (equalssign - card - lenroot > 7) ++ { ++ return (*status=BAD_KEYCHAR); ++ } + strncat(keyindex, &card[lenroot], equalssign - card - lenroot); /* copy suffix */ + + tstatus = 0; +@@ -1682,6 +1694,10 @@ + equalssign = strchr(card, '='); + if (equalssign == 0) continue; /* keyword has no value */ + ++ if (equalssign - card - lenroot > 7) ++ { ++ return (*status=BAD_KEYCHAR); ++ } + strncat(keyindex, &card[lenroot], equalssign - card - lenroot); /* copy suffix */ + + tstatus = 0; +@@ -1759,6 +1775,10 @@ + equalssign = strchr(card, '='); + if (equalssign == 0) continue; /* keyword has no value */ + ++ if (equalssign - card - lenroot > 7) ++ { ++ return (*status=BAD_KEYCHAR); ++ } + strncat(keyindex, &card[lenroot], equalssign - card - lenroot); /* copy suffix */ + + tstatus = 0; +@@ -1835,6 +1855,10 @@ + equalssign = strchr(card, '='); + if (equalssign == 0) continue; /* keyword has no value */ + ++ if (equalssign - card - lenroot > 7) ++ { ++ return (*status=BAD_KEYCHAR); ++ } + strncat(keyindex, &card[lenroot], equalssign - card - lenroot); /* copy suffix */ + tstatus = 0; + if (ffc2ii(keyindex, &ival, &tstatus) <= 0) /* test suffix */ +diff -ur cfitsio3430/group.c cfitsio3440/group.c +--- cfitsio3430/group.c 2018-03-01 17:28:51.000000000 +0100 ++++ cfitsio3440/group.c 2018-04-11 16:33:41.000000000 +0200 +@@ -1543,6 +1543,13 @@ + else + { + strcpy(memberLocation,cwd); ++ if (strlen(memberLocation)+strlen(memberFileName)+1 > ++ FLEN_FILENAME-1) ++ { ++ ffpmsg("member path and filename is too long (ffgtam)"); ++ *status = URL_PARSE_ERROR; ++ continue; ++ } + strcat(memberLocation,"/"); + strcat(memberLocation,memberFileName); + } +@@ -1565,6 +1572,14 @@ + else + { + strcpy(groupLocation,cwd); ++ if (strlen(groupLocation)+strlen(groupFileName)+1 > ++ FLEN_FILENAME-1) ++ { ++ ffpmsg("group path and filename is too long (ffgtam)"); ++ *status = URL_PARSE_ERROR; ++ continue; ++ } ++ + strcat(groupLocation,"/"); + strcat(groupLocation,groupFileName); + } +@@ -1782,11 +1797,18 @@ + + /* make grplc absolute if necessary */ + if(!fits_is_url_absolute(grplc)) { +- fits_path2url(grplc,groupLocation,status); ++ fits_path2url(grplc,FLEN_FILENAME,groupLocation,status); + + if(groupLocation[0] != '/') + { + strcpy(tmp, cwd); ++ if (strlen(tmp)+strlen(groupLocation)+1 > ++ FLEN_FILENAME-1) ++ { ++ ffpmsg("path and group location is too long (ffgtam)"); ++ *status = URL_PARSE_ERROR; ++ continue; ++ } + strcat(tmp,"/"); + strcat(tmp,groupLocation); + fits_clean_url(tmp,grplc,status); +@@ -1795,11 +1817,18 @@ + + /* make groupFileName absolute if necessary */ + if(!fits_is_url_absolute(groupFileName)) { +- fits_path2url(groupFileName,groupLocation,status); ++ fits_path2url(groupFileName,FLEN_FILENAME,groupLocation,status); + + if(groupLocation[0] != '/') + { + strcpy(tmp, cwd); ++ if (strlen(tmp)+strlen(groupLocation)+1 > ++ FLEN_FILENAME-1) ++ { ++ ffpmsg("path and group location is too long (ffgtam)"); ++ *status = URL_PARSE_ERROR; ++ continue; ++ } + strcat(tmp,"/"); + strcat(tmp,groupLocation); + /* +@@ -2145,7 +2174,14 @@ + &locationCol,&uriCol,&grptype,status); + + if(*status != 0) continue; ++ ++ /* verify the column formats */ ++ ++ *status = ffvcfm(gfptr,xtensionCol,extnameCol,extverCol,positionCol, ++ locationCol,uriCol,status); + ++ if(*status != 0) continue; ++ + /* + extract the member information from grouping table + */ +@@ -2415,6 +2451,13 @@ + { + fits_get_cwd(cwd,status); + strcat(cwd,"/"); ++ if (strlen(cwd)+strlen(grpLocation1)+1 > ++ FLEN_FILENAME-1) ++ { ++ ffpmsg("cwd and group location1 is too long (ffgmop)"); ++ *status = URL_PARSE_ERROR; ++ continue; ++ } + strcat(cwd,grpLocation1); + strcpy(grpLocation1,cwd); + } +@@ -2477,6 +2520,13 @@ + *grpLocation2 != '/') + { + fits_get_cwd(cwd,status); ++ if (strlen(cwd)+strlen(grpLocation2)+1 > ++ FLEN_FILENAME-1) ++ { ++ ffpmsg("cwd and group location2 is too long (ffgmop)"); ++ *status = URL_PARSE_ERROR; ++ continue; ++ } + strcat(cwd,"/"); + strcat(cwd,grpLocation2); + strcpy(grpLocation2,cwd); +@@ -3112,6 +3162,13 @@ + !fits_is_url_absolute(grpLocation1)) + { + strcpy(grpLocation3,cwd); ++ if (strlen(grpLocation3)+strlen(grpLocation1)+1 > ++ FLEN_FILENAME-1) ++ { ++ ffpmsg("group locations are too long (ffgmrm)"); ++ *status = URL_PARSE_ERROR; ++ continue; ++ } + strcat(grpLocation3,"/"); + strcat(grpLocation3,grpLocation1); + fits_clean_url(grpLocation3,grpLocation1,status); +@@ -3121,6 +3178,13 @@ + !fits_is_url_absolute(grpLocation2)) + { + strcpy(grpLocation3,cwd); ++ if (strlen(grpLocation3)+strlen(grpLocation2)+1 > ++ FLEN_FILENAME-1) ++ { ++ ffpmsg("group locations are too long (ffgmrm)"); ++ *status = URL_PARSE_ERROR; ++ continue; ++ } + strcat(grpLocation3,"/"); + strcat(grpLocation3,grpLocation2); + fits_clean_url(grpLocation3,grpLocation2,status); +@@ -3225,6 +3289,13 @@ + *editLocation = '\0'; + } + ++ if (strlen(grpLocation3)+strlen(grplc)+1 > ++ FLEN_FILENAME-1) ++ { ++ ffpmsg("group locations are too long (ffgmrm)"); ++ *status = URL_PARSE_ERROR; ++ continue; ++ } + strcat(grpLocation3,"/"); + strcat(grpLocation3,grplc); + *status = fits_clean_url(grpLocation3,grplc, +@@ -3466,6 +3537,87 @@ + } + + /*****************************************************************************/ ++int ffvcfm(fitsfile *gfptr, int xtensionCol, int extnameCol, int extverCol, ++ int positionCol, int locationCol, int uriCol, int *status) ++{ ++/* ++ Perform validation on column formats to ensure this matches the grouping ++ format the get functions expect. Particularly want to check widths of ++ string columns. ++*/ ++ ++ int typecode=0; ++ long repeat=0, width=0; ++ ++ if (*status != 0) return (*status); ++ ++ do { ++ if (xtensionCol) ++ { ++ fits_get_coltype(gfptr, xtensionCol, &typecode, &repeat, &width, status); ++ if (*status || typecode != TSTRING || repeat != width || repeat > 8) ++ { ++ if (*status==0) *status=NOT_GROUP_TABLE; ++ ffpmsg("Wrong format for Grouping xtension col. (ffvcfm)"); ++ continue; ++ } ++ } ++ if (extnameCol) ++ { ++ fits_get_coltype(gfptr, extnameCol, &typecode, &repeat, &width, status); ++ if (*status || typecode != TSTRING || repeat != width || repeat > 32) ++ { ++ if (*status==0) *status=NOT_GROUP_TABLE; ++ ffpmsg("Wrong format for Grouping name col. (ffvcfm)"); ++ continue; ++ } ++ } ++ if (extverCol) ++ { ++ fits_get_coltype(gfptr, extverCol, &typecode, &repeat, &width, status); ++ if (*status || typecode != TINT32BIT || repeat > 1) ++ { ++ if (*status==0) *status=NOT_GROUP_TABLE; ++ ffpmsg("Wrong format for Grouping version col. (ffvcfm)"); ++ continue; ++ } ++ } ++ if (positionCol) ++ { ++ fits_get_coltype(gfptr, positionCol, &typecode, &repeat, &width, status); ++ if (*status || typecode != TINT32BIT || repeat > 1) ++ { ++ if (*status==0) *status=NOT_GROUP_TABLE; ++ ffpmsg("Wrong format for Grouping position col. (ffvcfm)"); ++ continue; ++ } ++ } ++ if (locationCol) ++ { ++ fits_get_coltype(gfptr, locationCol, &typecode, &repeat, &width, status); ++ if (*status || typecode != TSTRING || repeat != width || repeat > 256) ++ { ++ if (*status==0) *status=NOT_GROUP_TABLE; ++ ffpmsg("Wrong format for Grouping location col. (ffvcfm)"); ++ continue; ++ } ++ } ++ if (uriCol) ++ { ++ fits_get_coltype(gfptr, uriCol, &typecode, &repeat, &width, status); ++ if (*status || typecode != TSTRING || repeat != width || repeat > 3) ++ { ++ if (*status==0) *status=NOT_GROUP_TABLE; ++ ffpmsg("Wrong format for Grouping URI col. (ffvcfm)"); ++ continue; ++ } ++ } ++ } while (0); ++ return (*status); ++} ++ ++ ++/*****************************************************************************/ + int ffgtdc(int grouptype, /* code specifying the type of + grouping table information: + GT_ID_ALL_URI 0 ==> defualt (all columns) +@@ -3977,11 +4129,17 @@ + + else if(!fits_is_url_absolute(location)) + { +- fits_path2url(location,tmpLocation,status); ++ fits_path2url(location,FLEN_FILENAME,tmpLocation,status); + + if(*tmpLocation != '/') + { + fits_get_cwd(cwd,status); ++ if (strlen(cwd)+strlen(tmpLocation)+1 > ++ FLEN_FILENAME-1) ++ { ++ ffpmsg("cwd and location are too long (ffgmf)"); ++ return (*status = URL_PARSE_ERROR); ++ } + strcat(cwd,"/"); + strcat(cwd,tmpLocation); + fits_clean_url(cwd,tmpLocation,status); +@@ -4084,6 +4242,13 @@ + *mbrLocation1 != '/') + { + fits_get_cwd(cwd,status); ++ if (strlen(cwd)+strlen(mbrLocation1)+1 > ++ FLEN_FILENAME-1) ++ { ++ ffpmsg("cwd and member locations are too long (ffgmf)"); ++ *status = URL_PARSE_ERROR; ++ continue; ++ } + strcat(cwd,"/"); + strcat(cwd,mbrLocation1); + fits_clean_url(cwd,mbrLocation1,status); +@@ -4094,6 +4259,13 @@ + *mbrLocation2 != '/') + { + fits_get_cwd(cwd,status); ++ if (strlen(cwd)+strlen(mbrLocation2)+1 > ++ FLEN_FILENAME-1) ++ { ++ ffpmsg("cwd and member locations are too long (ffgmf)"); ++ *status = URL_PARSE_ERROR; ++ continue; ++ } + strcat(cwd,"/"); + strcat(cwd,mbrLocation2); + fits_clean_url(cwd,mbrLocation2,status); +@@ -4119,6 +4291,13 @@ + if(!fits_is_url_absolute(grpLocation1) && *grpLocation1 != '/') + { + fits_get_cwd(cwd,status); ++ if (strlen(cwd)+strlen(grpLocation1)+1 > ++ FLEN_FILENAME-1) ++ { ++ ffpmsg("cwd and group locations are too long (ffgmf)"); ++ *status = URL_PARSE_ERROR; ++ continue; ++ } + strcat(cwd,"/"); + strcat(cwd,grpLocation1); + fits_clean_url(cwd,grpLocation1,status); +@@ -4154,6 +4333,13 @@ + if(!fits_is_url_absolute(grpLocation2) && *grpLocation2 != '/') + { + fits_get_cwd(cwd,status); ++ if (strlen(cwd)+strlen(grpLocation2)+1 > ++ FLEN_FILENAME-1) ++ { ++ ffpmsg("cwd and group locations are too long (ffgmf)"); ++ *status = URL_PARSE_ERROR; ++ continue; ++ } + strcat(cwd,"/"); + strcat(cwd,grpLocation2); + fits_clean_url(cwd,grpLocation2,status); +@@ -4823,6 +5009,8 @@ + Host dependent directory path to/from URL functions + --------------------------------------------------------------------------*/ + int fits_path2url(char *inpath, /* input file path string */ ++ int maxlength, /* I max number of chars that can be written ++ to output, including terminating NULL */ + char *outpath, /* output file path string */ + int *status) + /* +@@ -5187,7 +5375,7 @@ + encode all "unsafe" and "reserved" URL characters + */ + +- *status = fits_encode_url(buff,outpath,status); ++ *status = fits_encode_url(buff,maxlength,outpath,status); + + return(*status); + } +@@ -5473,7 +5661,12 @@ + Good old getcwd() seems to work with all other platforms + */ + +- getcwd(buff,FLEN_FILENAME); ++ if (!getcwd(buff,FLEN_FILENAME)) ++ { ++ cwd[0]=0; ++ ffpmsg("Path and file name too long (fits_get_cwd)"); ++ return (*status=URL_PARSE_ERROR); ++ } + + #endif + +@@ -5481,7 +5674,7 @@ + convert the cwd string to a URL standard path string + */ + +- fits_path2url(buff,cwd,status); ++ fits_path2url(buff,FLEN_FILENAME,cwd,status); + + return(*status); + } +@@ -5788,7 +5981,7 @@ + i = 0; + } + +- *status = fits_path2url(tmpPtr,realURL+i,status); ++ *status = fits_path2url(tmpPtr,FLEN_FILENAME-i,realURL+i,status); + } + } + +@@ -5810,7 +6003,7 @@ + i = 0; + } + +- *status = fits_path2url(tmpPtr,startURL+i,status); ++ *status = fits_path2url(tmpPtr,FLEN_FILENAME-i,startURL+i,status); + } + } + +@@ -5923,6 +6116,14 @@ + * onto output string until stack is empty */ + while(0 < mystack->stack_size) { + tmp = shift_grp_stack(mystack); ++ if (strlen(outURL) + strlen(tmp) + 1 > FLEN_FILENAME-1) ++ { ++ outURL[0]=0; ++ ffpmsg("outURL is too long (fits_clean_url)"); ++ *status = URL_PARSE_ERROR; ++ delete_grp_stack(&mystack); ++ return *status; ++ } + strcat(outURL, tmp); + strcat(outURL, "/"); + } +@@ -6118,10 +6319,25 @@ + */ + + for(j = refcount; j < refsize; ++j) +- if(refURL[j] == '/') strcat(relURL,"../"); ++ if(refURL[j] == '/') ++ { ++ if (strlen(relURL)+3 > FLEN_FILENAME-1) ++ { ++ *status = URL_PARSE_ERROR; ++ ffpmsg("relURL too long (fits_url2relurl)"); ++ return (*status); ++ } ++ strcat(relURL,"../"); ++ } + + /* copy all remaining characters of absURL to the output relURL */ + ++ if (strlen(relURL) + strlen(absURL+abscount) > FLEN_FILENAME-1) ++ { ++ *status = URL_PARSE_ERROR; ++ ffpmsg("relURL too long (fits_url2relurl)"); ++ return (*status); ++ } + strcat(relURL,absURL+abscount); + + /* we are done building the relative URL */ +@@ -6168,6 +6384,13 @@ + make a copy of the reference URL string refURL for parsing purposes + */ + ++ if (strlen(refURL) > FLEN_FILENAME-1) ++ { ++ absURL[0]=0; ++ ffpmsg("ref URL is too long (fits_relurl2url)"); ++ *status = URL_PARSE_ERROR; ++ continue; ++ } + strcpy(tmpStr,refURL); + + /* +@@ -6199,6 +6422,13 @@ + if(tmpStr1 != NULL) tmpStr1[1] = 0; + else tmpStr[0] = 0; + ++ if (strlen(tmpStr)+strlen(relURL) > FLEN_FILENAME-1) ++ { ++ absURL[0]=0; ++ ffpmsg("rel + ref URL is too long (fits_relurl2url)"); ++ *status = URL_PARSE_ERROR; ++ continue; ++ } + strcat(tmpStr,relURL); + } + else +@@ -6220,7 +6450,17 @@ + + strcpy(absURL,"/"); + +- for(i = 0; relURL[i] == '/'; ++i) strcat(absURL,"/"); ++ for(i = 0; relURL[i] == '/'; ++i) ++ { ++ if (strlen(absURL) + 1 > FLEN_FILENAME-1) ++ { ++ absURL[0]=0; ++ ffpmsg("abs URL is too long (fits_relurl2url)"); ++ *status = URL_PARSE_ERROR; ++ return (*status); ++ } ++ strcat(absURL,"/"); ++ } + + /* + loop over the refURL string until the slash pattern stored +@@ -6265,6 +6505,13 @@ + the absURL + */ + ++ if (strlen(tmpStr)+strlen(relURL) > FLEN_FILENAME-1) ++ { ++ absURL[0]=0; ++ ffpmsg("rel + ref URL is too long (fits_relurl2url)"); ++ *status = URL_PARSE_ERROR; ++ continue; ++ } + strcat(tmpStr,relURL); + } + +@@ -6280,7 +6527,9 @@ + return(*status); + } + /*--------------------------------------------------------------------------*/ +-int fits_encode_url(char *inpath, /* I URL to be encoded */ ++int fits_encode_url(char *inpath, /* I URL to be encoded */ ++ int maxlength, /* I max number of chars that may be copied ++ to outpath, including terminating NULL. */ + char *outpath, /* O output encoded URL */ + int *status) + /* +@@ -6288,12 +6537,12 @@ + convention, where XX stand for the two hexidecimal digits of the + encode character's ASCII code. + +- Note that the output path is at least as large as, if not larger than +- the input path, so that OUTPATH should be passed to this function +- with room for growth. If not a runtime error could result. It is +- assumed that OUTPATH has been allocated with enough room to hold +- the resulting encoded URL. +- ++ Note that the outpath length, as specified by the maxlength argument, ++ should be at least as large as inpath and preferably larger (to hold ++ any characters that need encoding). If more than maxlength chars are ++ required for outpath, including the terminating NULL, outpath will ++ be set to size 0 and an error status will be returned. ++ + This function was adopted from code in the libwww.a library available + via the W3 consortium + */ +@@ -6303,6 +6552,7 @@ + char *p; + char *q; + char *hex = "0123456789ABCDEF"; ++ int iout=0; + + unsigned const char isAcceptable[96] = + {/* 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xA 0xB 0xC 0xD 0xE 0xF */ +@@ -6325,7 +6575,7 @@ + + /* loop over all characters in inpath until '\0' is encountered */ + +- for(q = outpath, p = inpath; *p; p++) ++ for(q = outpath, p = inpath; *p && (iout < maxlength-1) ; p++) + { + a = (unsigned char)*p; + +@@ -6333,19 +6583,41 @@ + + if(!( a>=32 && a<128 && (isAcceptable[a-32]))) + { +- /* add a '%' character to the outpath */ +- *q++ = HEX_ESCAPE; +- /* add the most significant ASCII code hex value */ +- *q++ = hex[a >> 4]; +- /* add the least significant ASCII code hex value */ +- *q++ = hex[a & 15]; ++ if (iout+2 < maxlength-1) ++ { ++ /* add a '%' character to the outpath */ ++ *q++ = HEX_ESCAPE; ++ /* add the most significant ASCII code hex value */ ++ *q++ = hex[a >> 4]; ++ /* add the least significant ASCII code hex value */ ++ *q++ = hex[a & 15]; ++ iout += 3; ++ } ++ else ++ { ++ ffpmsg("URL input is too long to encode (fits_encode_url)"); ++ *status = URL_PARSE_ERROR; ++ outpath[0] = 0; ++ return (*status); ++ } + } + /* else just copy the character as is */ +- else *q++ = *p; ++ else ++ { ++ *q++ = *p; ++ iout++; ++ } + } + + /* null terminate the outpath string */ + ++ if (*p && (iout == maxlength-1)) ++ { ++ ffpmsg("URL input is too long to encode (fits_encode_url)"); ++ *status = URL_PARSE_ERROR; ++ outpath[0] = 0; ++ return (*status); ++ } + *q++ = 0; + + return(*status); +diff -ur cfitsio3430/group.h cfitsio3440/group.h +--- cfitsio3430/group.h 2018-03-01 17:28:51.000000000 +0100 ++++ cfitsio3440/group.h 2018-04-11 16:33:41.000000000 +0200 +@@ -23,6 +23,9 @@ + int *positionCol, int *locationCol, int *uriCol, int *grptype, + int *status); + ++int ffvcfm(fitsfile *gfptr, int xtensionCol, int extnameCol, int extverCol, ++ int positionCol, int locationCol, int uriCol, int *status); ++ + int ffgmul(fitsfile *mfptr, int rmopt, int *status); + + int ffgmf(fitsfile *gfptr, char *xtension, char *extname, int extver, +@@ -41,7 +44,7 @@ + + void prepare_keyvalue(char *keyvalue); + +-int fits_path2url(char *inpath, char *outpath, int *status); ++int fits_path2url(char *inpath, int maxlength, char *outpath, int *status); + + int fits_url2path(char *inpath, char *outpath, int *status); + +@@ -57,7 +60,7 @@ + + int fits_url2relurl(char *refURL, char *absURL, char *relURL, int *status); + +-int fits_encode_url(char *inpath, char *outpath, int *status); ++int fits_encode_url(char *inpath, int maxlength, char *outpath, int *status); + + int fits_unencode_url(char *inpath, char *outpath, int *status); + +diff -ur cfitsio3430/histo.c cfitsio3440/histo.c +--- cfitsio3430/histo.c 2018-03-01 17:28:51.000000000 +0100 ++++ cfitsio3440/histo.c 2018-04-11 16:33:41.000000000 +0200 +@@ -58,7 +58,7 @@ + most other reasonable combinations are supported. + */ + int ii, slen, defaulttype; +- char *ptr, tmpname[30], *file_expr = NULL; ++ char *ptr, tmpname[FLEN_VALUE], *file_expr = NULL; + double dummy; + + if (*status > 0) +@@ -369,52 +369,78 @@ + the column name, histogram min and max values, and bin size. + */ + { +- int slen, isanumber; +- char token[FLEN_VALUE]; ++ int slen, isanumber=0; ++ char *token=0; + + if (*status > 0) + return(*status); + +- slen = fits_get_token(ptr, " ,=:;", token, &isanumber); /* get 1st token */ ++ slen = fits_get_token2(ptr, " ,=:;", &token, &isanumber, status); /* get 1st token */ + +- if (slen == 0 && (**ptr == '\0' || **ptr == ',' || **ptr == ';') ) ++ if ((*status) || (slen == 0 && (**ptr == '\0' || **ptr == ',' || **ptr == ';')) ) + return(*status); /* a null range string */ +- ++ + if (!isanumber && **ptr != ':') + { + /* this looks like the column name */ +- +- if (token[0] == '#' && isdigit((int) token[1]) ) ++ ++ /* Check for case where col name string is empty but '=' ++ is still there (indicating a following specification string). ++ Musn't enter this block as token would not have been allocated. */ ++ if (slen != 0 || (**ptr != '=')) + { +- /* omit the leading '#' in the column number */ +- strcpy(colname, token+1); +- } +- else +- strcpy(colname, token); +- +- while (**ptr == ' ') /* skip over blanks */ +- (*ptr)++; ++ if (strlen(token) > FLEN_VALUE-1) ++ { ++ ffpmsg("column name too long (ffbinr)"); ++ free(token); ++ return(*status=PARSE_SYNTAX_ERR); ++ } ++ if (token[0] == '#' && isdigit((int) token[1]) ) ++ { ++ /* omit the leading '#' in the column number */ ++ strcpy(colname, token+1); ++ } ++ else ++ strcpy(colname, token); ++ free(token); ++ token=0; ++ while (**ptr == ' ') /* skip over blanks */ ++ (*ptr)++; + +- if (**ptr != '=') +- return(*status); /* reached the end */ ++ if (**ptr != '=') ++ return(*status); /* reached the end */ + ++ } + (*ptr)++; /* skip over the = sign */ + + while (**ptr == ' ') /* skip over blanks */ + (*ptr)++; + +- slen = fits_get_token(ptr, " ,:;", token, &isanumber); /* get token */ ++ /* get specification info */ ++ slen = fits_get_token2(ptr, " ,:;", &token, &isanumber, status); ++ if (*status) ++ return(*status); + } + + if (**ptr != ':') + { +- /* this is the first token, and since it is not followed by */ +- /* a ':' this must be the binsize token */ ++ /* This is the first token, and since it is not followed by ++ a ':' this must be the binsize token. Or it could be empty ++ in which case none of the following operations will do anything */ + if (!isanumber) ++ { ++ if (strlen(token) > FLEN_VALUE-1) ++ { ++ ffpmsg("binname too long (ffbinr)"); ++ free(token); ++ return(*status=PARSE_SYNTAX_ERR); ++ } + strcpy(binname, token); ++ } + else + *binsizein = strtod(token, NULL); +- ++ ++ free(token); + return(*status); /* reached the end */ + } + else +@@ -423,37 +449,73 @@ + if (slen) + { + if (!isanumber) ++ { ++ if (strlen(token) > FLEN_VALUE-1) ++ { ++ ffpmsg("minname too long (ffbinr)"); ++ free(token); ++ return(*status=PARSE_SYNTAX_ERR); ++ } + strcpy(minname, token); ++ } + else + *minin = strtod(token, NULL); ++ free(token); ++ token=0; + } + } + + (*ptr)++; /* skip the colon between the min and max values */ +- slen = fits_get_token(ptr, " ,:;", token, &isanumber); /* get token */ ++ slen = fits_get_token2(ptr, " ,:;", &token, &isanumber, status); /* get token */ ++ if (*status) ++ return(*status); + + /* the token contains the max value */ + if (slen) + { + if (!isanumber) ++ { ++ if (strlen(token) > FLEN_VALUE-1) ++ { ++ ffpmsg("maxname too long (ffbinr)"); ++ free(token); ++ return(*status=PARSE_SYNTAX_ERR); ++ } + strcpy(maxname, token); ++ } + else + *maxin = strtod(token, NULL); ++ free(token); ++ token=0; + } + + if (**ptr != ':') ++ { ++ free(token); + return(*status); /* reached the end; no binsize token */ ++ } + + (*ptr)++; /* skip the colon between the max and binsize values */ +- slen = fits_get_token(ptr, " ,:;", token, &isanumber); /* get token */ ++ slen = fits_get_token2(ptr, " ,:;", &token, &isanumber, status); /* get token */ ++ if (*status) ++ return(*status); + + /* the token contains the binsize value */ + if (slen) + { + if (!isanumber) ++ { ++ if (strlen(token) > FLEN_VALUE-1) ++ { ++ ffpmsg("binname too long (ffbinr)"); ++ free(token); ++ return(*status=PARSE_SYNTAX_ERR); ++ } + strcpy(binname, token); ++ } + else + *binsizein = strtod(token, NULL); ++ free(token); + } + + return(*status); +@@ -935,7 +997,7 @@ + > 0) + { + strcpy(errmsg, "column for histogram axis doesn't exist: "); +- strcat(errmsg, colname[ii]); ++ strncat(errmsg, colname[ii], FLEN_ERRMSG-strlen(errmsg)-1); + ffpmsg(errmsg); + return(*status); + } +@@ -947,7 +1009,7 @@ + if (repeat > 1) + { + strcpy(errmsg, "Can't bin a vector column: "); +- strcat(errmsg, colname[ii]); ++ strncat(errmsg, colname[ii],FLEN_ERRMSG-strlen(errmsg)-1); + ffpmsg(errmsg); + return(*status = BAD_DATATYPE); + } +@@ -959,7 +1021,7 @@ + if (datatype < 0 || datatype == TSTRING) + { + strcpy(errmsg, "Inappropriate datatype; can't bin this column: "); +- strcat(errmsg, colname[ii]); ++ strncat(errmsg, colname[ii],FLEN_ERRMSG-strlen(errmsg)-1); + ffpmsg(errmsg); + return(*status = BAD_DATATYPE); + } +@@ -970,14 +1032,14 @@ + if (minin[ii] == DOUBLENULLVALUE) + { + ffkeyn("TLMIN", histData.hcolnum[ii], keyname, status); +- if (ffgky(*fptr, TFLOAT, keyname, amin+ii, NULL, status) > 0) ++ if (ffgky(*fptr, TFLOAT, keyname, amin+ii, NULL, status) > 0) + { + /* use actual data minimum value for the histogram minimum */ + *status = 0; + if (fits_get_col_minmax(*fptr, histData.hcolnum[ii], amin+ii, &datamax, status) > 0) + { + strcpy(errmsg, "Error calculating datamin and datamax for column: "); +- strcat(errmsg, colname[ii]); ++ strncat(errmsg, colname[ii],FLEN_ERRMSG-strlen(errmsg)-1); + ffpmsg(errmsg); + return(*status); + } +@@ -1004,7 +1066,7 @@ + if (fits_get_col_minmax(*fptr, histData.hcolnum[ii], &datamin, &amax[ii], status) > 0) + { + strcpy(errmsg, "Error calculating datamin and datamax for column: "); +- strcat(errmsg, colname[ii]); ++ strncat(errmsg, colname[ii],FLEN_ERRMSG-strlen(errmsg)-1); + ffpmsg(errmsg); + return(*status); + } +@@ -1543,7 +1646,7 @@ + > 0) + { + strcpy(errmsg, "column for histogram axis doesn't exist: "); +- strcat(errmsg, colname[ii]); ++ strncat(errmsg, colname[ii],FLEN_ERRMSG-strlen(errmsg)-1); + ffpmsg(errmsg); + return(*status); + } +@@ -1558,7 +1661,7 @@ + if (repeat > 1) + { + strcpy(errmsg, "Can't bin a vector column: "); +- strcat(errmsg, colname[ii]); ++ strncat(errmsg, colname[ii],FLEN_ERRMSG-strlen(errmsg)-1); + ffpmsg(errmsg); + return(*status = BAD_DATATYPE); + } +@@ -1570,7 +1673,7 @@ + if (datatype < 0 || datatype == TSTRING) + { + strcpy(errmsg, "Inappropriate datatype; can't bin this column: "); +- strcat(errmsg, colname[ii]); ++ strncat(errmsg, colname[ii],FLEN_ERRMSG-strlen(errmsg)-1); + ffpmsg(errmsg); + return(*status = BAD_DATATYPE); + } +@@ -1593,19 +1696,19 @@ + + if (minin[ii] != DOUBLENULLVALUE) + { +- amin[ii] = (float) minin[ii]; ++ amin[ii] = (float) minin[ii]; + } + else + { + ffkeyn("TLMIN", colnum[ii], keyname, status); +- if (ffgky(fptr, TFLOAT, keyname, amin+ii, NULL, status) > 0) ++ if (ffgky(fptr, TFLOAT, keyname, amin+ii, NULL, status) > 0) + { + /* use actual data minimum value for the histogram minimum */ + *status = 0; + if (fits_get_col_minmax(fptr, colnum[ii], amin+ii, &datamax, status) > 0) + { + strcpy(errmsg, "Error calculating datamin and datamax for column: "); +- strcat(errmsg, colname[ii]); ++ strncat(errmsg, colname[ii],FLEN_ERRMSG-strlen(errmsg)-1); + ffpmsg(errmsg); + return(*status); + } +@@ -1645,7 +1748,7 @@ + if (fits_get_col_minmax(fptr, colnum[ii], &datamin, &amax[ii], status) > 0) + { + strcpy(errmsg, "Error calculating datamin and datamax for column: "); +- strcat(errmsg, colname[ii]); ++ strncat(errmsg, colname[ii],FLEN_ERRMSG-strlen(errmsg)-1); + ffpmsg(errmsg); + return(*status); + } +diff -ur cfitsio3430/imcompress.c cfitsio3440/imcompress.c +--- cfitsio3430/imcompress.c 2018-03-01 17:28:51.000000000 +0100 ++++ cfitsio3440/imcompress.c 2018-04-11 16:33:41.000000000 +0200 +@@ -162,8 +162,8 @@ + static int fits_unshuffle_2bytes(char *heap, LONGLONG length, int *status); + + static int fits_int_to_longlong_inplace(int *intarray, long length, int *status); +-static int fits_short_to_int_inplace(short *intarray, long length, int *status); +-static int fits_ushort_to_int_inplace(unsigned short *intarray, long length, int *status); ++static int fits_short_to_int_inplace(short *intarray, long length, int shift, int *status); ++static int fits_ushort_to_int_inplace(unsigned short *intarray, long length, int shift, int *status); + static int fits_sbyte_to_int_inplace(signed char *intarray, long length, int *status); + static int fits_ubyte_to_int_inplace(unsigned char *intarray, long length, int *status); + +@@ -2252,7 +2252,7 @@ + } else { /* just do the data type conversion to int */ + /* have to convert sbuff to an I*4 array, in place */ + /* sbuff must have been allocated large enough to do this */ +- fits_short_to_int_inplace(sbuff, tilelen, status); ++ fits_short_to_int_inplace(sbuff, tilelen, 0, status); + } + } else { + /* have to convert to int if using PLIO */ +@@ -2273,10 +2273,10 @@ + else + idata[ii] = (int) sbuff[ii] + 32768; + } +- } else { /* just do the data type conversion to int */ ++ } else { + /* have to convert sbuff to an I*4 array, in place */ + /* sbuff must have been allocated large enough to do this */ +- fits_short_to_int_inplace(sbuff, tilelen, status); ++ fits_short_to_int_inplace(sbuff, tilelen, 32768, status); + } + } else { + /* This is not an unsigned 16-bit integer array, so process normally */ +@@ -2292,7 +2292,7 @@ + } else { /* just do the data type conversion to int */ + /* have to convert sbuff to an I*4 array, in place */ + /* sbuff must have been allocated large enough to do this */ +- fits_short_to_int_inplace(sbuff, tilelen, status); ++ fits_short_to_int_inplace(sbuff, tilelen, 0, status); + } + } + } +@@ -2373,9 +2373,15 @@ + idata[ii] = ((int) usbuff[ii]) - 32768; + } + } else { /* just do the data type conversion to int */ +- /* have to convert usbuff to an I*4 array, in place */ +- /* usbuff must have been allocated large enough to do this */ +- fits_ushort_to_int_inplace(usbuff, tilelen, status); ++ /* for HCOMPRESS we need to simply subtract 32768 */ ++ /* for PLIO, have to convert usbuff to an I*4 array, in place */ ++ /* usbuff must have been allocated large enough to do this */ ++ ++ if ((outfptr->Fptr)->compress_type == HCOMPRESS_1) { ++ fits_ushort_to_int_inplace(usbuff, tilelen, -32768, status); ++ } else { ++ fits_ushort_to_int_inplace(usbuff, tilelen, 0, status); ++ } + } + } + +@@ -8397,7 +8403,7 @@ + cratio[ii] = uncompressed_size / compressed_size; + + snprintf(tempstring,FLEN_VALUE," r=%6.2f",cratio[ii]); +- strcat(results[ii],tempstring); ++ strncat(results[ii],tempstring, 29-strlen(results[ii])); + + /* now we just have to compress the array of descriptors (both input and output) */ + /* and write them to the output table. */ +@@ -8524,7 +8530,7 @@ + cratio[ii] = (float) datasize / (float) dlen; /* compression ratio of the column */ + + snprintf(tempstring,FLEN_VALUE," r=%6.2f",cratio[ii]); +- strcat(results[ii],tempstring); ++ strncat(results[ii],tempstring,29-strlen(results[ii])); + + } /* end of not a virtual column */ + } /* end of loop over columns */ +@@ -9508,7 +9514,7 @@ + return(*status); + } + /*--------------------------------------------------------------------------*/ +-static int fits_short_to_int_inplace(short *shortarray, long length, int *status) ++static int fits_short_to_int_inplace(short *shortarray, long length, int shift, int *status) + + /* convert the input array of 16-bit integers into an array of 32-bit integers, + in place. This will overwrite the input array with the new longer array starting +@@ -9549,7 +9555,7 @@ + + /* do datatype conversion into temp array */ + for (ii = 0; ii < ntodo; ii++) { +- intarray[ii] = shortarray[ii + firstelem]; ++ intarray[ii] = (int)(shortarray[ii + firstelem]) + shift; + } + + /* copy temp array back to alias */ +@@ -9572,7 +9578,7 @@ + } + /*--------------------------------------------------------------------------*/ + static int fits_ushort_to_int_inplace(unsigned short *ushortarray, long length, +- int *status) ++ int shift, int *status) + + /* convert the input array of 16-bit unsigned integers into an array of 32-bit integers, + in place. This will overwrite the input array with the new longer array starting +@@ -9613,7 +9619,7 @@ + + /* do datatype conversion into temp array */ + for (ii = 0; ii < ntodo; ii++) { +- intarray[ii] = ushortarray[ii + firstelem]; ++ intarray[ii] = (int)(ushortarray[ii + firstelem]) + shift; + } + + /* copy temp array back to alias */ +diff -ur cfitsio3430/iraffits.c cfitsio3440/iraffits.c +--- cfitsio3430/iraffits.c 2018-03-01 17:28:51.000000000 +0100 ++++ cfitsio3440/iraffits.c 2018-04-11 16:33:41.000000000 +0200 +@@ -1329,6 +1329,8 @@ + /* Translate value from ASCII to binary */ + if (value != NULL) { + minint = -MAXINT - 1; ++ if (strlen(value) > 29) ++ return(0); + strcpy (val, value); + dval = atof (val); + if (dval+0.001 > MAXINT) +@@ -1372,10 +1374,13 @@ + lval = strlen (value); + if (lval < lstr) + strcpy (str, value); +- else if (lstr > 1) ++ else if (lstr > 1) { + strncpy (str, value, lstr-1); +- else ++ str[lstr-1]=0; ++ } ++ else { + str[0] = value[0]; ++ } + return (1); + } + else +@@ -1422,6 +1427,7 @@ + + /* Find length of variable name */ + strncpy (keyword,keyword0, sizeof(keyword)-1); ++ keyword[80]=0; + brack1 = strsrch (keyword,lbracket); + if (brack1 == NULL) + brack1 = strsrch (keyword,comma); +--- cfitsio3430/modkey.c 2018-03-01 17:28:51.000000000 +0100 ++++ cfitsio3440/modkey.c 2018-04-11 16:33:42.000000000 +0200 +@@ -754,6 +754,11 @@ + contin = 0; + while (remain > 0) + { ++ if (nchar > FLEN_VALUE-1) ++ { ++ ffpmsg("longstr keyword value is too long (ffmkls)"); ++ return (*status=BAD_KEYCHAR); ++ } + strncpy(tstring, &value[next], nchar); /* copy string to temp buff */ + tstring[nchar] = '\0'; + ffs2c(tstring, valstring, status); /* put quotes around the string */ +@@ -984,9 +989,19 @@ + + strcpy(valstring, "(" ); + ffr2f(value[0], decim, tmpstring, status); /* convert to string */ ++ if (strlen(tmpstring)+3 > FLEN_VALUE-1) ++ { ++ ffpmsg("complex key value too long (ffmkfc)"); ++ return(*status=BAD_F2C); ++ } + strcat(valstring, tmpstring); + strcat(valstring, ", "); + ffr2f(value[1], decim, tmpstring, status); /* convert to string */ ++ if (strlen(valstring) + strlen(tmpstring)+1 > FLEN_VALUE-1) ++ { ++ ffpmsg("complex key value too long (ffmkfc)"); ++ return(*status=BAD_F2C); ++ } + strcat(valstring, tmpstring); + strcat(valstring, ")"); + +@@ -1019,9 +1034,19 @@ + + strcpy(valstring, "(" ); + ffr2e(value[0], decim, tmpstring, status); /* convert to string */ ++ if (strlen(tmpstring)+3 > FLEN_VALUE-1) ++ { ++ ffpmsg("complex key value too long (ffmkyc)"); ++ return(*status=BAD_F2C); ++ } + strcat(valstring, tmpstring); + strcat(valstring, ", "); + ffr2e(value[1], decim, tmpstring, status); /* convert to string */ ++ if (strlen(valstring) + strlen(tmpstring)+1 > FLEN_VALUE-1) ++ { ++ ffpmsg("complex key value too long (ffmkyc)"); ++ return(*status=BAD_F2C); ++ } + strcat(valstring, tmpstring); + strcat(valstring, ")"); + +@@ -1054,9 +1079,19 @@ + + strcpy(valstring, "(" ); + ffd2f(value[0], decim, tmpstring, status); /* convert to string */ ++ if (strlen(tmpstring)+3 > FLEN_VALUE-1) ++ { ++ ffpmsg("complex key value too long (ffmkfm)"); ++ return(*status=BAD_F2C); ++ } + strcat(valstring, tmpstring); + strcat(valstring, ", "); + ffd2f(value[1], decim, tmpstring, status); /* convert to string */ ++ if (strlen(valstring) + strlen(tmpstring)+1 > FLEN_VALUE-1) ++ { ++ ffpmsg("complex key value too long (ffmkfm)"); ++ return(*status=BAD_F2C); ++ } + strcat(valstring, tmpstring); + strcat(valstring, ")"); + +@@ -1089,9 +1124,19 @@ + + strcpy(valstring, "(" ); + ffd2e(value[0], decim, tmpstring, status); /* convert to string */ ++ if (strlen(tmpstring)+3 > FLEN_VALUE-1) ++ { ++ ffpmsg("complex key value too long (ffmkym)"); ++ return(*status=BAD_F2C); ++ } + strcat(valstring, tmpstring); + strcat(valstring, ", "); + ffd2e(value[1], decim, tmpstring, status); /* convert to string */ ++ if (strlen(valstring) + strlen(tmpstring)+1 > FLEN_VALUE-1) ++ { ++ ffpmsg("complex key value too long (ffmkym)"); ++ return(*status=BAD_F2C); ++ } + strcat(valstring, tmpstring); + strcat(valstring, ")"); + +@@ -1208,6 +1253,11 @@ + contin = 0; + while (remain > 0) + { ++ if (nchar > FLEN_VALUE-1) ++ { ++ ffpmsg("longstr keyword value is too long (ffikls)"); ++ return (*status=BAD_KEYCHAR); ++ } + strncpy(tstring, &value[next], nchar); /* copy string to temp buff */ + tstring[nchar] = '\0'; + ffs2c(tstring, valstring, status); /* put quotes around the string */ +@@ -1379,9 +1429,19 @@ + + strcpy(valstring, "(" ); + ffr2f(value[0], decim, tmpstring, status); /* convert to string */ ++ if (strlen(tmpstring)+3 > FLEN_VALUE-1) ++ { ++ ffpmsg("complex key value too long (ffikfc)"); ++ return(*status=BAD_F2C); ++ } + strcat(valstring, tmpstring); + strcat(valstring, ", "); + ffr2f(value[1], decim, tmpstring, status); /* convert to string */ ++ if (strlen(valstring) + strlen(tmpstring)+1 > FLEN_VALUE-1) ++ { ++ ffpmsg("complex key value too long (ffikfc)"); ++ return(*status=BAD_F2C); ++ } + strcat(valstring, tmpstring); + strcat(valstring, ")"); + +@@ -1406,9 +1466,19 @@ + + strcpy(valstring, "(" ); + ffr2e(value[0], decim, tmpstring, status); /* convert to string */ ++ if (strlen(tmpstring)+3 > FLEN_VALUE-1) ++ { ++ ffpmsg("complex key value too long (ffikyc)"); ++ return(*status=BAD_F2C); ++ } + strcat(valstring, tmpstring); + strcat(valstring, ", "); + ffr2e(value[1], decim, tmpstring, status); /* convert to string */ ++ if (strlen(valstring) + strlen(tmpstring)+1 > FLEN_VALUE-1) ++ { ++ ffpmsg("complex key value too long (ffikyc)"); ++ return(*status=BAD_F2C); ++ } + strcat(valstring, tmpstring); + strcat(valstring, ")"); + +@@ -1434,9 +1504,19 @@ + + strcpy(valstring, "(" ); + ffd2f(value[0], decim, tmpstring, status); /* convert to string */ ++ if (strlen(tmpstring)+3 > FLEN_VALUE-1) ++ { ++ ffpmsg("complex key value too long (ffikfm)"); ++ return(*status=BAD_F2C); ++ } + strcat(valstring, tmpstring); + strcat(valstring, ", "); + ffd2f(value[1], decim, tmpstring, status); /* convert to string */ ++ if (strlen(valstring) + strlen(tmpstring)+1 > FLEN_VALUE-1) ++ { ++ ffpmsg("complex key value too long (ffikfm)"); ++ return(*status=BAD_F2C); ++ } + strcat(valstring, tmpstring); + strcat(valstring, ")"); + +@@ -1461,9 +1541,19 @@ + + strcpy(valstring, "(" ); + ffd2e(value[0], decim, tmpstring, status); /* convert to string */ ++ if (strlen(tmpstring)+3 > FLEN_VALUE-1) ++ { ++ ffpmsg("complex key value too long (ffikym)"); ++ return(*status=BAD_F2C); ++ } + strcat(valstring, tmpstring); + strcat(valstring, ", "); + ffd2e(value[1], decim, tmpstring, status); /* convert to string */ ++ if (strlen(valstring) + strlen(tmpstring)+1 > FLEN_VALUE-1) ++ { ++ ffpmsg("complex key value too long (ffikym)"); ++ return(*status=BAD_F2C); ++ } + strcat(valstring, tmpstring); + strcat(valstring, ")"); + +@@ -1531,6 +1621,11 @@ + keylength = strcspn(buff2, "="); + if (keylength == 80) keylength = 8; + ++ /* test for the common commentary keywords which by definition have 8-char names */ ++ if ( !fits_strncasecmp( "COMMENT ", buff2, 8) || !fits_strncasecmp( "HISTORY ", buff2, 8) || ++ !fits_strncasecmp( " ", buff2, 8) || !fits_strncasecmp( "CONTINUE", buff2, 8) ) ++ keylength = 8; ++ + for (ii=0; ii < keylength; ii++) /* make sure keyword name is uppercase */ + buff2[ii] = toupper(buff2[ii]); + +diff -ur cfitsio3430/putcol.c cfitsio3440/putcol.c +--- cfitsio3430/putcol.c 2018-03-01 17:28:51.000000000 +0100 ++++ cfitsio3440/putcol.c 2018-04-11 16:33:42.000000000 +0200 +@@ -855,7 +855,8 @@ + */ + { + col->fptr = fptr; +- strcpy(col->colname, colname); ++ strncpy(col->colname, colname,69); ++ col->colname[69]=0; + col->colnum = 0; /* set column number undefined since name is given */ + col->datatype = datatype; + col->iotype = iotype; +@@ -894,7 +895,8 @@ + set iterator column parameter + */ + { +- strcpy(col->colname, colname); ++ strncpy(col->colname, colname,69); ++ col->colname[69]=0; + col->colnum = 0; /* set column number undefined since name is given */ + return(0); + } +diff -ur cfitsio3430/putkey.c cfitsio3440/putkey.c +--- cfitsio3430/putkey.c 2018-03-01 17:28:51.000000000 +0100 ++++ cfitsio3440/putkey.c 2018-04-11 16:33:42.000000000 +0200 +@@ -724,9 +724,19 @@ + + strcpy(valstring, "(" ); + ffr2e(value[0], decim, tmpstring, status); /* convert to string */ ++ if (strlen(valstring)+strlen(tmpstring)+2 > FLEN_VALUE-1) ++ { ++ ffpmsg("Error converting complex to string (ffpkyc)"); ++ return(*status=BAD_F2C); ++ } + strcat(valstring, tmpstring); + strcat(valstring, ", "); + ffr2e(value[1], decim, tmpstring, status); /* convert to string */ ++ if (strlen(valstring)+strlen(tmpstring)+1 > FLEN_VALUE-1) ++ { ++ ffpmsg("Error converting complex to string (ffpkyc)"); ++ return(*status=BAD_F2C); ++ } + strcat(valstring, tmpstring); + strcat(valstring, ")"); + +@@ -755,9 +765,19 @@ + + strcpy(valstring, "(" ); + ffd2e(value[0], decim, tmpstring, status); /* convert to string */ ++ if (strlen(valstring)+strlen(tmpstring)+2 > FLEN_VALUE-1) ++ { ++ ffpmsg("Error converting complex to string (ffpkym)"); ++ return(*status=BAD_F2C); ++ } + strcat(valstring, tmpstring); + strcat(valstring, ", "); + ffd2e(value[1], decim, tmpstring, status); /* convert to string */ ++ if (strlen(valstring)+strlen(tmpstring)+1 > FLEN_VALUE-1) ++ { ++ ffpmsg("Error converting complex to string (ffpkym)"); ++ return(*status=BAD_F2C); ++ } + strcat(valstring, tmpstring); + strcat(valstring, ")"); + +@@ -786,9 +806,19 @@ + + strcpy(valstring, "(" ); + ffr2f(value[0], decim, tmpstring, status); /* convert to string */ ++ if (strlen(valstring)+strlen(tmpstring)+2 > FLEN_VALUE-1) ++ { ++ ffpmsg("Error converting complex to string (ffpkfc)"); ++ return(*status=BAD_F2C); ++ } + strcat(valstring, tmpstring); + strcat(valstring, ", "); + ffr2f(value[1], decim, tmpstring, status); /* convert to string */ ++ if (strlen(valstring)+strlen(tmpstring)+1 > FLEN_VALUE-1) ++ { ++ ffpmsg("Error converting complex to string (ffpkfc)"); ++ return(*status=BAD_F2C); ++ } + strcat(valstring, tmpstring); + strcat(valstring, ")"); + +@@ -817,9 +847,19 @@ + + strcpy(valstring, "(" ); + ffd2f(value[0], decim, tmpstring, status); /* convert to string */ ++ if (strlen(valstring)+strlen(tmpstring)+2 > FLEN_VALUE-1) ++ { ++ ffpmsg("Error converting complex to string (ffpkfm)"); ++ return(*status=BAD_F2C); ++ } + strcat(valstring, tmpstring); + strcat(valstring, ", "); + ffd2f(value[1], decim, tmpstring, status); /* convert to string */ ++ if (strlen(valstring)+strlen(tmpstring)+1 > FLEN_VALUE-1) ++ { ++ ffpmsg("Error converting complex to string (ffpkfm)"); ++ return(*status=BAD_F2C); ++ } + strcat(valstring, tmpstring); + strcat(valstring, ")"); + +@@ -858,6 +898,11 @@ + ffd2f(fraction, 16, fstring, status); /* convert to 16 decimal string */ + + cptr = strchr(fstring, '.'); /* find the decimal point */ ++ if (strlen(valstring)+strlen(cptr) > FLEN_VALUE-1) ++ { ++ ffpmsg("converted numerical string too long"); ++ return(*status=BAD_F2C); ++ } + strcat(valstring, cptr); /* append the fraction to the integer */ + + ffmkky(keyname, valstring, comm, card, status); /* construct the keyword*/ +@@ -1990,6 +2035,12 @@ + } + + snprintf(value, 80,"%ld", naxes[ii]); ++ /* This will either be followed by a ',' or ')'. */ ++ if (strlen(tdimstr)+strlen(value)+1 > FLEN_VALUE-1) ++ { ++ ffpmsg("TDIM string too long (ffptdm)"); ++ return(*status = BAD_TDIM); ++ } + strcat(tdimstr, value); /* append the axis size */ + + totalpix *= naxes[ii]; +@@ -2087,7 +2138,12 @@ + /* sprintf is platform dependent ( %lld, %ld, %I64d ) */ + + snprintf(value, 80, "%.0f", (double) naxes[ii]); +- ++ ++ if (strlen(tdimstr)+strlen(value)+1 > FLEN_VALUE-1) ++ { ++ ffpmsg("TDIM string too long (ffptdmll)"); ++ return(*status = BAD_TDIM); ++ } + strcat(tdimstr, value); /* append the axis size */ + + totalpix *= naxes[ii]; +@@ -2464,6 +2520,12 @@ + ffkeyn("TBCOL", ii + 1, name, status); + ffpkyj(fptr, name, tbcol[ii], comm, status); + ++ if (strlen(tform[ii]) > 29) ++ { ++ ffpmsg("Error: ASCII table TFORM code is too long (ffphtb)"); ++ *status = BAD_TFORM; ++ break; ++ } + strcpy(tfmt, tform[ii]); /* required TFORMn keyword */ + ffupch(tfmt); + ffkeyn("TFORM", ii + 1, name, status); +@@ -2582,6 +2644,12 @@ + ffpkys(fptr, name, ttype[ii], comm, status); + } + ++ if (strlen(tform[ii]) > 29) ++ { ++ ffpmsg("Error: BIN table TFORM code is too long (ffphbn)"); ++ *status = BAD_TFORM; ++ break; ++ } + strcpy(tfmt, tform[ii]); /* required TFORMn keyword */ + ffupch(tfmt); + +@@ -2915,7 +2983,7 @@ + return(*status = BAD_DECIM); + } + +- if (sprintf(cval, "%.*f", decim, fval) < 0) ++ if (snprintf(cval, FLEN_VALUE,"%.*f", decim, fval) < 0) + { + ffpmsg("Error in ffr2f converting float to string"); + *status = BAD_F2C; +@@ -2951,7 +3019,7 @@ + + if (decim < 0) + { /* use G format if decim is negative */ +- if ( sprintf(cval, "%.*G", -decim, fval) < 0) ++ if ( snprintf(cval, FLEN_VALUE,"%.*G", -decim, fval) < 0) + { + ffpmsg("Error in ffr2e converting float to string"); + *status = BAD_F2C; +@@ -2962,7 +3030,7 @@ + if ( !strchr(cval, '.') && strchr(cval,'E') ) + { + /* reformat value with a decimal point and single zero */ +- if ( sprintf(cval, "%.1E", fval) < 0) ++ if ( snprintf(cval, FLEN_VALUE,"%.1E", fval) < 0) + { + ffpmsg("Error in ffr2e converting float to string"); + *status = BAD_F2C; +@@ -2974,7 +3042,7 @@ + } + else + { +- if ( sprintf(cval, "%.*E", decim, fval) < 0) ++ if ( snprintf(cval, FLEN_VALUE,"%.*E", decim, fval) < 0) + { + ffpmsg("Error in ffr2e converting float to string"); + *status = BAD_F2C; +@@ -2992,7 +3060,7 @@ + ffpmsg("Error in ffr2e: float value is a NaN or INDEF"); + *status = BAD_F2C; + } +- else if ( !strchr(cval, '.') && !strchr(cval,'E') ) ++ else if ( !strchr(cval, '.') && !strchr(cval,'E') && strlen(cval) < FLEN_VALUE-1 ) + { + /* add decimal point if necessary to distinquish from integer */ + strcat(cval, "."); +@@ -3023,7 +3091,7 @@ + return(*status = BAD_DECIM); + } + +- if (sprintf(cval, "%.*f", decim, dval) < 0) ++ if (snprintf(cval, FLEN_VALUE,"%.*f", decim, dval) < 0) + { + ffpmsg("Error in ffd2f converting double to string"); + *status = BAD_F2C; +@@ -3059,7 +3127,7 @@ + + if (decim < 0) + { /* use G format if decim is negative */ +- if ( sprintf(cval, "%.*G", -decim, dval) < 0) ++ if ( snprintf(cval, FLEN_VALUE,"%.*G", -decim, dval) < 0) + { + ffpmsg("Error in ffd2e converting float to string"); + *status = BAD_F2C; +@@ -3070,7 +3138,7 @@ + if ( !strchr(cval, '.') && strchr(cval,'E') ) + { + /* reformat value with a decimal point and single zero */ +- if ( sprintf(cval, "%.1E", dval) < 0) ++ if ( snprintf(cval, FLEN_VALUE,"%.1E", dval) < 0) + { + ffpmsg("Error in ffd2e converting float to string"); + *status = BAD_F2C; +@@ -3082,7 +3150,7 @@ + } + else + { +- if ( sprintf(cval, "%.*E", decim, dval) < 0) ++ if ( snprintf(cval, FLEN_VALUE,"%.*E", decim, dval) < 0) + { + ffpmsg("Error in ffd2e converting float to string"); + *status = BAD_F2C; +@@ -3100,7 +3168,7 @@ + ffpmsg("Error in ffd2e: double value is a NaN or INDEF"); + *status = BAD_F2C; + } +- else if ( !strchr(cval, '.') && !strchr(cval,'E') ) ++ else if ( !strchr(cval, '.') && !strchr(cval,'E') && strlen(cval) < FLEN_VALUE-1) + { + /* add decimal point if necessary to distinquish from integer */ + strcat(cval, "."); +diff -ur cfitsio3430/windumpexts.c cfitsio3440/windumpexts.c +--- cfitsio3430/windumpexts.c 2018-03-01 17:28:51.000000000 +0100 ++++ cfitsio3440/windumpexts.c 2018-04-11 16:33:42.000000000 +0200 +@@ -232,7 +232,8 @@ + symbol[8] = 0; + } else { + s = stringTable + pSymbolTable->N.Name.Long; +- strcpy(symbol, s); ++ strncpy(symbol, s, 1023); ++ symbol[1023]=0; + } + s = symbol; + f = strchr(s, '@'); +@@ -485,7 +486,8 @@ + } + pos = 0; + for (i = 0; i < arg; i++) { +- strcpy(&cmdline[pos], argv[i]); ++ strncpy(&cmdline[pos], argv[i], 9999-pos); ++ cmdline[9999]=0; + pos += strlen(&cmdline[pos]) + 1; + fargv[i] = argv[i]; + } diff --git a/cfitsio.spec b/cfitsio.spec index 4794546..36aac38 100644 --- a/cfitsio.spec +++ b/cfitsio.spec @@ -1,6 +1,6 @@ Name: cfitsio Version: 3.430 -Release: 1%{?dist} +Release: 2%{?dist} Summary: Library for manipulating FITS data files License: MIT @@ -12,6 +12,8 @@ Patch0: cfitsio-zlib.patch Patch1: cfitsio-noversioncheck.patch # Some rearrangements in pkg-config file Patch2: cfitsio-pkgconfig.patch +# Backport of security changes from 3.44, to avoid API changes +Patch3: cfitsio-backport344.patch BuildRequires: gcc-gfortran zlib-devel BuildRequires: bzip2-devel @@ -80,6 +82,7 @@ compression algorithm. %patch0 -p1 %patch1 -p1 %patch2 -p1 +%patch3 -p1 # remove bundled zlib # not all the files inside zlib belong to zlib @@ -142,6 +145,9 @@ chmod 755 %{buildroot}%{_bindir}/f{,un}pack %{_bindir}/funpack %changelog +* Mon Mar 04 2019 Sergio Pascual - 3.430-2 +- Backport security fixes from 3.440 (rhbz #1570484) + * Mon Mar 12 2018 Christian Dersch - 3.430-1 - new version