Source: imagemagick Version: 8:6.8.9.9-7 Severity: wishlist Tags: patch User: reproducible-bui...@lists.alioth.debian.org Usertags: toolchain X-Debbugs-Cc: reproducible-bui...@lists.alioth.debian.org
Dear Maintainer, While working on the “reproducible builds” effort [1], we have noticed that some packages (like kannel) use 'convert' in their building process, resulting in timestamps in postscript files that break reproducibility. To solve this kind of issues, it would be nice to have 'convert' support the SOURCE_DATE_EPOCH environment variable [2]. See the attached patch for a solution to this issue. Regards, Alexis Bienvenüe. [1] https://wiki.debian.org/ReproducibleBuilds [2] https://reproducible-builds.org/specs/source-date-epoch/
diff -Nru imagemagick-6.8.9.9/debian/changelog imagemagick-6.8.9.9/debian/changelog --- imagemagick-6.8.9.9/debian/changelog 2016-01-17 21:45:14.000000000 +0100 +++ imagemagick-6.8.9.9/debian/changelog 2016-04-01 01:11:37.000000000 +0200 @@ -1,3 +1,10 @@ +imagemagick (8:6.8.9.9-7.0reproducible1) unstable; urgency=medium + + * Honour the SOURCE_DATE_EPOCH environment variable + when writing to files. + + -- Alexis Bienvenüe <p...@passoire.fr> Fri, 01 Apr 2016 01:11:36 +0200 + imagemagick (8:6.8.9.9-7) unstable; urgency=low * Fix various minor security issues diff -Nru imagemagick-6.8.9.9/debian/patches/0076-Honour-SOURCE_DATE_EPOCH-when-writing.patch imagemagick-6.8.9.9/debian/patches/0076-Honour-SOURCE_DATE_EPOCH-when-writing.patch --- imagemagick-6.8.9.9/debian/patches/0076-Honour-SOURCE_DATE_EPOCH-when-writing.patch 1970-01-01 01:00:00.000000000 +0100 +++ imagemagick-6.8.9.9/debian/patches/0076-Honour-SOURCE_DATE_EPOCH-when-writing.patch 2016-04-03 15:48:56.000000000 +0200 @@ -0,0 +1,307 @@ +Description: Honour the SOURCE_DATE_EPOCH variable when writing + Honour the SOURCE_DATE_EPOCH environment variable when writing + to some files: PS, PDF, PNG, CIN, DPX, MAT, PDB, setting the timestamp + to the provided value and using GM time instead of local time. +Author: Alexis Bienvenüe <p...@passoire.fr> + +Index: imagemagick-6.8.9.9/coders/cin.c +=================================================================== +--- imagemagick-6.8.9.9.orig/coders/cin.c ++++ imagemagick-6.8.9.9/coders/cin.c +@@ -971,14 +971,14 @@ static MagickBooleanType WriteCINImage(c + sizeof(cin.file.filename)); + offset+=WriteBlob(image,sizeof(cin.file.filename),(unsigned char *) + cin.file.filename); +- seconds=time((time_t *) NULL); +-#if defined(MAGICKCORE_HAVE_LOCALTIME_R) +- (void) localtime_r(&seconds,&local_time); +-#else +- (void) memcpy(&local_time,localtime(&seconds),sizeof(local_time)); +-#endif ++ seconds=CurrentTime(); ++ (void) LocalOrGMTime(&seconds,&local_time); + (void) memset(timestamp,0,sizeof(timestamp)); +- (void) strftime(timestamp,MaxTextExtent,"%Y:%m:%d:%H:%M:%S%Z",&local_time); ++ if(getenv("SOURCE_DATE_EPOCH")) { ++ (void) strftime(timestamp,MaxTextExtent,"%Y:%m:%d:%H:%M:%SUTC",&local_time); ++ } else { ++ (void) strftime(timestamp,MaxTextExtent,"%Y:%m:%d:%H:%M:%S%Z",&local_time); ++ } + (void) memset(cin.file.create_date,0,sizeof(cin.file.create_date)); + (void) CopyMagickString(cin.file.create_date,timestamp,11); + offset+=WriteBlob(image,sizeof(cin.file.create_date),(unsigned char *) +@@ -1074,9 +1074,6 @@ static MagickBooleanType WriteCINImage(c + sizeof(cin.origination.filename)); + offset+=WriteBlob(image,sizeof(cin.origination.filename),(unsigned char *) + cin.origination.filename); +- seconds=time((time_t *) NULL); +- (void) memset(timestamp,0,sizeof(timestamp)); +- (void) strftime(timestamp,MaxTextExtent,"%Y:%m:%d:%H:%M:%S%Z",&local_time); + (void) memset(cin.origination.create_date,0, + sizeof(cin.origination.create_date)); + (void) CopyMagickString(cin.origination.create_date,timestamp,11); +Index: imagemagick-6.8.9.9/coders/dpx.c +=================================================================== +--- imagemagick-6.8.9.9.orig/coders/dpx.c ++++ imagemagick-6.8.9.9/coders/dpx.c +@@ -1535,7 +1535,7 @@ static MagickBooleanType WriteDPXImage(c + (void) strncpy(dpx.file.filename,value,sizeof(dpx.file.filename)-1); + offset+=WriteBlob(image,sizeof(dpx.file.filename),(unsigned char *) + dpx.file.filename); +- seconds=time((time_t *) NULL); ++ seconds=CurrentTime(); + (void) FormatMagickTime(seconds,sizeof(dpx.file.timestamp), + dpx.file.timestamp); + offset+=WriteBlob(image,sizeof(dpx.file.timestamp),(unsigned char *) +Index: imagemagick-6.8.9.9/coders/mat.c +=================================================================== +--- imagemagick-6.8.9.9.orig/coders/mat.c ++++ imagemagick-6.8.9.9/coders/mat.c +@@ -1224,12 +1224,8 @@ static MagickBooleanType WriteMATImage(c + return(MagickFalse); + image->depth=8; + +- current_time=time((time_t *) NULL); +-#if defined(MAGICKCORE_HAVE_LOCALTIME_R) +- (void) localtime_r(¤t_time,&local_time); +-#else +- (void) memcpy(&local_time,localtime(¤t_time),sizeof(local_time)); +-#endif ++ current_time=CurrentTime(); ++ (void) LocalOrGMTime(¤t_time,&local_time); + (void) memset(MATLAB_HDR,' ',MagickMin(sizeof(MATLAB_HDR),124)); + FormatLocaleString(MATLAB_HDR,sizeof(MATLAB_HDR), + "MATLAB 5.0 MAT-file, Platform: %s, Created on: %s %s %2d %2d:%2d:%2d %d", +Index: imagemagick-6.8.9.9/coders/pdb.c +=================================================================== +--- imagemagick-6.8.9.9.orig/coders/pdb.c ++++ imagemagick-6.8.9.9/coders/pdb.c +@@ -762,7 +762,7 @@ static MagickBooleanType WritePDBImage(c + (void) CopyMagickString(pdb_info.name,image_info->filename,32); + pdb_info.attributes=0; + pdb_info.version=0; +- pdb_info.create_time=time(NULL); ++ pdb_info.create_time=CurrentTime(); + pdb_info.modify_time=pdb_info.create_time; + pdb_info.archive_time=0; + pdb_info.modify_number=0; +Index: imagemagick-6.8.9.9/coders/pdf.c +=================================================================== +--- imagemagick-6.8.9.9.orig/coders/pdf.c ++++ imagemagick-6.8.9.9/coders/pdf.c +@@ -1261,7 +1261,7 @@ RestoreMSCWarning + value=GetImageProperty(image,"date:create"); + if (value != (const char *) NULL) + (void) CopyMagickString(create_date,value,MaxTextExtent); +- (void) FormatMagickTime(time((time_t *) NULL),MaxTextExtent,timestamp); ++ (void) FormatMagickTime(CurrentTime(),MaxTextExtent,timestamp); + i=FormatLocaleString(xmp_profile,MaxTextExtent,XMPProfile, + XMPProfileMagick,modify_date,create_date,timestamp, + GetMagickVersion(&version),EscapeParenthesis(basename), +@@ -2669,12 +2669,8 @@ RestoreMSCWarning + (void) FormatLocaleString(buffer,MaxTextExtent,"/Title (%s)\n", + EscapeParenthesis(basename)); + (void) WriteBlobString(image,buffer); +- seconds=time((time_t *) NULL); +-#if defined(MAGICKCORE_HAVE_LOCALTIME_R) +- (void) localtime_r(&seconds,&local_time); +-#else +- (void) memcpy(&local_time,localtime(&seconds),sizeof(local_time)); +-#endif ++ seconds=CurrentTime(); ++ (void) LocalOrGMTime(&seconds,&local_time); + (void) FormatLocaleString(date,MaxTextExtent,"D:%04d%02d%02d%02d%02d%02d", + local_time.tm_year+1900,local_time.tm_mon+1,local_time.tm_mday, + local_time.tm_hour,local_time.tm_min,local_time.tm_sec); +Index: imagemagick-6.8.9.9/coders/png.c +=================================================================== +--- imagemagick-6.8.9.9.orig/coders/png.c ++++ imagemagick-6.8.9.9/coders/png.c +@@ -7735,7 +7735,7 @@ static void write_tIME_chunk(Image *imag + } + else + { +- time(&ttime); ++ ttime=CurrentTime(); + png_convert_from_time_t(&ptime,ttime); + } + png_set_tIME(ping,info,&ptime); +Index: imagemagick-6.8.9.9/coders/ps.c +=================================================================== +--- imagemagick-6.8.9.9.orig/coders/ps.c ++++ imagemagick-6.8.9.9/coders/ps.c +@@ -1643,7 +1643,7 @@ static MagickBooleanType WritePSImage(co + (void) FormatLocaleString(buffer,MaxTextExtent,"%%%%Title: (%s)\n", + image->filename); + (void) WriteBlobString(image,buffer); +- timer=time((time_t *) NULL); ++ timer=CurrentTime(); + (void) FormatMagickTime(timer,MaxTextExtent,date); + (void) FormatLocaleString(buffer,MaxTextExtent, + "%%%%CreationDate: (%s)\n",date); +Index: imagemagick-6.8.9.9/coders/ps2.c +=================================================================== +--- imagemagick-6.8.9.9.orig/coders/ps2.c ++++ imagemagick-6.8.9.9/coders/ps2.c +@@ -568,7 +568,7 @@ static MagickBooleanType WritePS2Image(c + (void) FormatLocaleString(buffer,MaxTextExtent,"%%%%Title: (%s)\n", + image->filename); + (void) WriteBlobString(image,buffer); +- timer=time((time_t *) NULL); ++ timer=CurrentTime(); + (void) FormatMagickTime(timer,MaxTextExtent,date); + (void) FormatLocaleString(buffer,MaxTextExtent, + "%%%%CreationDate: (%s)\n",date); +Index: imagemagick-6.8.9.9/coders/ps3.c +=================================================================== +--- imagemagick-6.8.9.9.orig/coders/ps3.c ++++ imagemagick-6.8.9.9/coders/ps3.c +@@ -1008,7 +1008,7 @@ static MagickBooleanType WritePS3Image(c + (void) FormatLocaleString(buffer,MaxTextExtent,"%%%%Title: %s\n", + image->filename); + (void) WriteBlobString(image,buffer); +- timer=time((time_t *) NULL); ++ timer=CurrentTime(); + (void) FormatMagickTime(timer,MaxTextExtent,date); + (void) FormatLocaleString(buffer,MaxTextExtent, + "%%%%CreationDate: %s\n",date); +Index: imagemagick-6.8.9.9/magick/string.c +=================================================================== +--- imagemagick-6.8.9.9.orig/magick/string.c ++++ imagemagick-6.8.9.9/magick/string.c +@@ -1126,6 +1126,81 @@ MagickExport ssize_t FormatMagickSize(co + return(count); + } + ++/* CurrentTime() ++ returns the source time from SOURCE_DATE_EPOCH environment variable (if set), ++ or current time. ++ ++ code from https://wiki.debian.org/ReproducibleBuilds/TimestampsProposal#C ++*/ ++ ++MagickExport time_t CurrentTime() ++{ ++ struct tm *build_time; ++ time_t now; ++ unsigned long long epoch; ++ char *endptr; ++ char *source_date_epoch=getenv("SOURCE_DATE_EPOCH"); ++ if (source_date_epoch) { ++ errno = 0; ++ epoch = strtoull(source_date_epoch, &endptr, 10); ++ if ((errno == ERANGE && (epoch == ULLONG_MAX || epoch == 0)) ++ || (errno != 0 && epoch == 0)) { ++ fprintf(stderr, "Environment variable $SOURCE_DATE_EPOCH: strtoull: %s\n", strerror(errno)); ++ exit(EXIT_FAILURE); ++ } ++ if (endptr == source_date_epoch) { ++ fprintf(stderr, "Environment variable $SOURCE_DATE_EPOCH: No digits were found: %s\n", endptr); ++ exit(EXIT_FAILURE); ++ } ++ if (*endptr != '\0') { ++ fprintf(stderr, "Environment variable $SOURCE_DATE_EPOCH: Trailing garbage: %s\n", endptr); ++ exit(EXIT_FAILURE); ++ } ++ if (epoch > ULONG_MAX) { ++ fprintf(stderr, "Environment variable $SOURCE_DATE_EPOCH: value must be smaller than or equal to: %lu but was found to be: %llu \n", ULONG_MAX, epoch); ++ exit(EXIT_FAILURE); ++ } ++ now = epoch; ++ } else { ++ now = time(NULL); ++ } ++ return(now); ++} ++ ++/* LocalOrGMTime computes the local or GM time from s, and stores it to t. ++ If the SOURCE_DATE_EPOCH environment variable is set, GM time is used, ++ otherwise local time is used. ++ */ ++MagickExport void LocalOrGMTime(time_t *s,struct tm *t) { ++ if(getenv("SOURCE_DATE_EPOCH") != NULL) { ++#if defined(MAGICKCORE_HAVE_GMTIME_R) ++ (void) gmtime_r(s,t); ++#else ++ { ++ struct tm ++ *my_time; ++ ++ my_time=gmtime(t); ++ if (my_time != (struct tm *) NULL) ++ (void) memcpy(t,my_time,sizeof(*t)); ++ } ++#endif ++ } else { ++#if defined(MAGICKCORE_HAVE_LOCALTIME_R) ++ (void) localtime_r(s,t); ++#else ++ { ++ struct tm ++ *my_time; ++ ++ my_time=localtime(t); ++ if (my_time != (struct tm *) NULL) ++ (void) memcpy(t,my_time,sizeof(*t)); ++ } ++#endif ++ } ++} ++ + /* + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % % +@@ -1195,14 +1270,21 @@ MagickExport ssize_t FormatMagickTime(co + (void) memcpy(&gm_time,my_time,sizeof(gm_time)); + } + #endif +- timezone=(time_t) ((local_time.tm_min-gm_time.tm_min)/60+ +- local_time.tm_hour-gm_time.tm_hour+24*((local_time.tm_year- +- gm_time.tm_year) != 0 ? (local_time.tm_year-gm_time.tm_year) : +- (local_time.tm_yday-gm_time.tm_yday))); +- count=FormatLocaleString(timestamp,length, +- "%04d-%02d-%02dT%02d:%02d:%02d%+03ld:00",local_time.tm_year+1900, +- local_time.tm_mon+1,local_time.tm_mday,local_time.tm_hour, +- local_time.tm_min,local_time.tm_sec,(long) timezone); ++ if(getenv("SOURCE_DATE_EPOCH")) { ++ count=FormatLocaleString(timestamp,length, ++ "%04d-%02d-%02dT%02d:%02d:%02d%+03ld:00",gm_time.tm_year+1900, ++ gm_time.tm_mon+1,gm_time.tm_mday,gm_time.tm_hour, ++ gm_time.tm_min,gm_time.tm_sec,0); ++ } else { ++ timezone=(time_t) ((local_time.tm_min-gm_time.tm_min)/60+ ++ local_time.tm_hour-gm_time.tm_hour+24*((local_time.tm_year- ++ gm_time.tm_year) != 0 ? (local_time.tm_year-gm_time.tm_year) : ++ (local_time.tm_yday-gm_time.tm_yday))); ++ count=FormatLocaleString(timestamp,length, ++ "%04d-%02d-%02dT%02d:%02d:%02d%+03ld:00",local_time.tm_year+1900, ++ local_time.tm_mon+1,local_time.tm_mday,local_time.tm_hour, ++ local_time.tm_min,local_time.tm_sec,(long) timezone); ++ } + return(count); + } + +Index: imagemagick-6.8.9.9/magick/string_.h +=================================================================== +--- imagemagick-6.8.9.9.orig/magick/string_.h ++++ imagemagick-6.8.9.9/magick/string_.h +@@ -83,6 +83,9 @@ extern MagickExport ssize_t + FormatMagickSize(const MagickSizeType,const MagickBooleanType,char *), + FormatMagickTime(const time_t,const size_t,char *); + ++extern MagickExport time_t ++ CurrentTime(); ++ + extern MagickExport StringInfo + *AcquireStringInfo(const size_t), + *BlobToStringInfo(const void *,const size_t), +@@ -107,7 +110,8 @@ extern MagickExport void + SetStringInfoDatum(StringInfo *,const unsigned char *), + SetStringInfoLength(StringInfo *,const size_t), + SetStringInfoPath(StringInfo *,const char *), +- StripString(char *); ++ StripString(char *), ++ LocalOrGMTime(time_t *,struct tm *); + + #if defined(__cplusplus) || defined(c_plusplus) + } diff -Nru imagemagick-6.8.9.9/debian/patches/series imagemagick-6.8.9.9/debian/patches/series --- imagemagick-6.8.9.9/debian/patches/series 2016-01-17 21:45:18.000000000 +0100 +++ imagemagick-6.8.9.9/debian/patches/series 2016-04-01 01:05:19.000000000 +0200 @@ -74,3 +74,4 @@ 0073-Fixed-memory-leaks.patch 0074-Fix-overflow-in-pict-image-parsing.patch 0075-Fix-buffer-overflow-in-icon-parsing-code.patch +0076-Honour-SOURCE_DATE_EPOCH-when-writing.patch