From d76a902c877bcaa79d28008a0d51776fc9333621 Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 30 Nov 2016 00:48:28 +0000 Subject: [PATCH] Further changes to the date/time functions to suppress harmless signed integer overflow warnings that could have occurred when doing out-of-range date calculations which, according to the docs, give undefined results. FossilOrigin-Name: dc453b3403450b1d8cc53daf0721fed025b9053c --- manifest | 14 ++++++------- manifest.uuid | 2 +- src/date.c | 53 +++++++++++++++++++++++++++++++------------------- test/date.test | 49 +++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 89 insertions(+), 29 deletions(-) diff --git a/manifest b/manifest index 733c2d390f..aecf668d2a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C The\sdocumentation\ssays\sthat\sthe\sbuilt-in\sdate-time\sfunctions\sgive\sundefined\nresults\sfor\sdates\sbefore\s0000-01-01\sand\safter\s9999-12-31.\s\sChange\sthe\nactually\simplementation\sso\sthat\sthe\sanswer\sgiven\sis\sreally\sNULL.\s\sThis\salso\navoids\sunnecessary\shand-wringing\sover\san\ssigned\sinteger\soverflow\sthat\smight\notherwise\soccur\swhen\sprocessing\sout-of-bound\sdates. -D 2016-11-29T20:39:48.413 +C Further\schanges\sto\sthe\sdate/time\sfunctions\sto\ssuppress\sharmless\ssigned\ninteger\soverflow\swarnings\sthat\scould\shave\soccurred\swhen\sdoing\sout-of-range\ndate\scalculations\swhich,\saccording\sto\sthe\sdocs,\sgive\sundefined\sresults. +D 2016-11-30T00:48:28.495 F Makefile.in 6b572807415d3f0a379cebc9461416d8df4a12c8 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc bb4d970894abbbe0e88d00aac29bd52af8bc95f4 @@ -337,7 +337,7 @@ F src/build.c 178f16698cbcb43402c343a9413fe22c99ffee21 F src/callback.c 2e76147783386374bf01b227f752c81ec872d730 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e F src/ctime.c a2a52d6e353f459d8ab0f07321f60fafa47d5421 -F src/date.c 53a4019b90ae1c9cb990196eed0ed196d3f341e1 +F src/date.c 736c1f36c58bb137f64d8d1f72467dc027832563 F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d F src/delete.c cac97d1117a3008934da3a6a587b3608e65e1495 F src/expr.c 8c224aa28278a5c1eed55247b7a571ff388ad5c2 @@ -626,7 +626,7 @@ F test/csv01.test e0ba3caaa57e4c667a0b45977689fb8082f14348 F test/ctime.test ff6c38e822459d6ca743c34901caf57740b08b54 F test/cursorhint.test 7bc346788390475e77a345da2b92270d04d35856 F test/cursorhint2.test fa41f0d997e67db921d08c31e73111b32811201a -F test/date.test 47e7f7057c0efac0e5e26da2d7b6a9a128139de6 +F test/date.test a6a5a48b90907bca9fbcc79a30be5a715c1ab2fc F test/dbstatus.test 73149851b3aff14fc6db478e58f9083a66422cf5 F test/dbstatus2.test e93ab03bfae6d62d4d935f20de928c19ca0ed0ab F test/default.test 0cb49b1c315a0d81c81d775e407f66906a2a604d @@ -1535,7 +1535,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 9021f6875f897d8b609ebcc04162dc6e0b529a4a -R e95c6d806c4c5b465cfcf2c97462edc9 +P d410a839752153c6d8be08f758abfbc16475745a +R 4de84d6de9c4735ca2171beb11da6949 U drh -Z f7a67f418429ef937fb437b96080796e +Z a5d0c3a9dbd00d301630dafcf90b9504 diff --git a/manifest.uuid b/manifest.uuid index 95eeb46ae6..f5cb5e6a0d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d410a839752153c6d8be08f758abfbc16475745a \ No newline at end of file +dc453b3403450b1d8cc53daf0721fed025b9053c \ No newline at end of file diff --git a/src/date.c b/src/date.c index a90b5318b8..6a6743f86d 100644 --- a/src/date.c +++ b/src/date.c @@ -65,17 +65,17 @@ struct tm *__cdecl localtime(const time_t *); */ typedef struct DateTime DateTime; struct DateTime { - sqlite3_int64 iJD; /* The julian day number times 86400000 */ - int Y, M, D; /* Year, month, and day */ - int h, m; /* Hour and minutes */ - int tz; /* Timezone offset in minutes */ - double s; /* Seconds */ - char validYMD; /* True (1) if Y,M,D are valid */ - char validHMS; /* True (1) if h,m,s are valid */ - char validJD; /* True (1) if iJD is valid */ - char validTZ; /* True (1) if tz is valid */ - char tzSet; /* Timezone was set explicitly */ - char isError; /* An overflow has occurred */ + sqlite3_uint64 iJD; /* The julian day number times 86400000 */ + int Y, M, D; /* Year, month, and day */ + int h, m; /* Hour and minutes */ + int tz; /* Timezone offset in minutes */ + double s; /* Seconds */ + char validYMD; /* True (1) if Y,M,D are valid */ + char validHMS; /* True (1) if h,m,s are valid */ + char validJD; /* True (1) if iJD is valid */ + char validTZ; /* True (1) if tz is valid */ + char tzSet; /* Timezone was set explicitly */ + char isError; /* An overflow has occurred */ }; @@ -232,6 +232,14 @@ static int parseHhMmSs(const char *zDate, DateTime *p){ return 0; } +/* +** Put the DateTime object into its error state. +*/ +static void datetimeError(DateTime *p){ + memset(p, 0, sizeof(*p)); + p->isError = 1; +} + /* ** Convert from YYYY-MM-DD HH:MM:SS to julian day. We always assume ** that the YYYY-MM-DD is according to the Gregorian calendar. @@ -251,6 +259,10 @@ static void computeJD(DateTime *p){ M = 1; D = 1; } + if( Y<-4713 || Y>9999 ){ + datetimeError(p); + return; + } if( M<=2 ){ Y--; M += 12; @@ -373,7 +385,7 @@ static int parseDateOrTime( ** The input is the JulianDay times 86400000. */ static int validJulianDay(sqlite3_int64 iJD){ - return iJD>=148699540800000 && iJD<=464269060799999; + return iJD>=0 && iJD<=464269060799999; } /* @@ -387,8 +399,7 @@ static void computeYMD(DateTime *p){ p->M = 1; p->D = 1; }else if( !validJulianDay(p->iJD) ){ - memset(p, 0, sizeof(*p)); - p->isError = 1; + datetimeError(p); return; }else{ Z = (int)((p->iJD + 43200000)/86400000); @@ -713,6 +724,7 @@ static int parseModifier(sqlite3_context *pCtx, const char *zMod, DateTime *p){ case '8': case '9': { double rRounder; + double rAbs; for(n=1; z[n] && z[n]!=':' && !sqlite3Isspace(z[n]); n++){} if( !sqlite3AtoF(z, &r, n, SQLITE_UTF8) ){ rc = 1; @@ -749,15 +761,16 @@ static int parseModifier(sqlite3_context *pCtx, const char *zMod, DateTime *p){ computeJD(p); rc = 0; rRounder = r<0 ? -0.5 : +0.5; - if( n==3 && strcmp(z,"day")==0 ){ + rAbs = r<0 ? -r : r; + if( n==3 && strcmp(z,"day")==0 && rAbs<5373485.0 ){ p->iJD += (sqlite3_int64)(r*86400000.0 + rRounder); - }else if( n==4 && strcmp(z,"hour")==0 ){ + }else if( n==4 && strcmp(z,"hour")==0 && rAbs<128963628.0 ){ p->iJD += (sqlite3_int64)(r*(86400000.0/24.0) + rRounder); - }else if( n==6 && strcmp(z,"minute")==0 ){ + }else if( n==6 && strcmp(z,"minute")==0 && rAbs<7737817680.0 ){ p->iJD += (sqlite3_int64)(r*(86400000.0/(24.0*60.0)) + rRounder); - }else if( n==6 && strcmp(z,"second")==0 ){ + }else if( n==6 && strcmp(z,"second")==0 && rAbs<464269060800.0 ){ p->iJD += (sqlite3_int64)(r*(86400000.0/(24.0*60.0*60.0)) + rRounder); - }else if( n==5 && strcmp(z,"month")==0 ){ + }else if( n==5 && strcmp(z,"month")==0 && rAbs<176546.0 ){ int x, y; computeYMD_HMS(p); p->M += (int)r; @@ -770,7 +783,7 @@ static int parseModifier(sqlite3_context *pCtx, const char *zMod, DateTime *p){ if( y!=r ){ p->iJD += (sqlite3_int64)((r - y)*30.0*86400000.0 + rRounder); } - }else if( n==4 && strcmp(z,"year")==0 ){ + }else if( n==4 && strcmp(z,"year")==0 && rAbs<14713.0 ){ int y = (int)r; computeYMD_HMS(p); p->Y += y; diff --git a/test/date.test b/test/date.test index 0286bcc3bc..2d336e6c00 100644 --- a/test/date.test +++ b/test/date.test @@ -61,7 +61,7 @@ datetest 1.19 {julianday('2000-01-01 12:00:00.1')} 2451545.00000116 datetest 1.20 {julianday('2000-01-01 12:00:00.01')} 2451545.00000012 datetest 1.21 {julianday('2000-01-01 12:00:00.001')} 2451545.00000001 datetest 1.22 {julianday('2000-01-01 12:00:00.')} NULL -datetest 1.23 julianday(12345.6) NULL +datetest 1.23 julianday(12345.6) 12345.6 datetest 1.23b julianday(1721059.5) 1721059.5 datetest 1.24 {julianday('2001-01-01 12:00:00 bogus')} NULL datetest 1.25 {julianday('2001-01-01 bogus')} NULL @@ -418,6 +418,16 @@ datetest 8.19 {datetime('now','11.25 seconds')} {2003-10-22 12:34:11} datetest 8.90 {datetime('now','abcdefghijklmnopqrstuvwyxzABCDEFGHIJLMNOP')} NULL set sqlite_current_time 0 +# Negative years work. Example: '-4713-11-26' is JD 1.5. +# +datetest 9.1 {julianday('-4713-11-24 12:00:00')} {0.0} +datetest 9.2 {julianday(datetime(5))} {5.0} +datetest 9.3 {julianday(datetime(10))} {10.0} +datetest 9.4 {julianday(datetime(100))} {100.0} +datetest 9.5 {julianday(datetime(1000))} {1000.0} +datetest 9.6 {julianday(datetime(10000))} {10000.0} +datetest 9.7 {julianday(datetime(100000))} {100000.0} + # datetime() with just an HH:MM:SS correctly inserts the date 2000-01-01. # datetest 10.1 {datetime('01:02:03')} {2000-01-01 01:02:03} @@ -550,4 +560,41 @@ do_test date-15.2 { } } {1} +# Tests of extreme values in date/time functions. Run with UBSan or the +# equivalent to verify no signed interger overflow warnings. +# +datetest 16.1 {date(147483649)} NULL +datetest 16.2 {datetime(0)} {-4713-11-24 12:00:00} +datetest 16.3 {datetime(5373484.49999999)} {9999-12-31 23:59:59} +datetest 16.4 {julianday('-4713-11-24 12:00:00')} 0.0 +datetest 16.5 {julianday('9999-12-31 23:59:59.999')} 5373484.49999999 +datetest 16.6 {datetime(0,'+464269060799 seconds')} {9999-12-31 23:59:59} +datetest 16.7 {datetime(0,'+464269060800 seconds')} NULL +datetest 16.8 {datetime(0,'+7737817679 minutes')} {9999-12-31 23:59:00} +datetest 16.9 {datetime(0,'+7737817680 minutes')} NULL +datetest 16.10 {datetime(0,'+128963627 hours')} {9999-12-31 23:00:00} +datetest 16.11 {datetime(0,'+128963628 hours')} NULL +datetest 16.12 {datetime(0,'+5373484 days')} {9999-12-31 12:00:00} +datetest 16.13 {datetime(0,'+5373485 days')} NULL +datetest 16.14 {datetime(0,'+176545 months')} {9999-12-24 12:00:00} +datetest 16.15 {datetime(0,'+176546 months')} NULL +datetest 16.16 {datetime(0,'+14712 years')} {9999-11-24 12:00:00} +datetest 16.17 {datetime(0,'+14713 years')} NULL +datetest 16.20 {datetime(5373484.4999999,'-464269060799 seconds')} \ + {-4713-11-24 12:00:00} +datetest 16.21 {datetime(5373484,'-464269060800 seconds')} NULL +datetest 16.22 {datetime(5373484.4999999,'-7737817679 minutes')} \ + {-4713-11-24 12:00:59} +datetest 16.23 {datetime(5373484,'-7737817680 minutes')} NULL +datetest 16.24 {datetime(5373484.4999999,'-128963627 hours')} \ + {-4713-11-24 12:59:59} +datetest 16.25 {datetime(5373484,'-128963628 hours')} NULL +datetest 16.26 {datetime(5373484,'-5373484 days')} {-4713-11-24 12:00:00} +datetest 16.27 {datetime(5373484,'-5373485 days')} NULL +datetest 16.28 {datetime(5373484,'-176545 months')} {-4713-12-01 12:00:00} +datetest 16.29 {datetime(5373484,'-176546 months')} NULL +datetest 16.30 {datetime(5373484,'-14712 years')} {-4713-12-31 12:00:00} +datetest 16.31 {datetime(5373484,'-14713 years')} NULL + + finish_test