# HG changeset patch # User redestad # Date 1507816829 -7200 # Node ID 39d1de71facaaee272767e9cd7e1c2d64ac109ed # Parent eb010905ccb7f018677c6bf59c6e27c890225aaf 8188869: jdk9/10 reject zip/jar files where seconds value of timestamp is out of supported range 0 - 59 Reviewed-by: sherman, alanb diff -r eb010905ccb7 -r 39d1de71faca src/java.base/share/classes/java/util/zip/ZipUtils.java --- a/src/java.base/share/classes/java/util/zip/ZipUtils.java Thu Oct 12 13:35:41 2017 +0200 +++ b/src/java.base/share/classes/java/util/zip/ZipUtils.java Thu Oct 12 16:00:29 2017 +0200 @@ -28,9 +28,11 @@ import java.nio.file.attribute.FileTime; import java.security.AccessController; import java.security.PrivilegedAction; +import java.time.DateTimeException; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; +import java.util.Date; import java.util.concurrent.TimeUnit; import static java.util.zip.ZipConstants.ENDHDR; @@ -78,31 +80,39 @@ } /** + /* * Converts DOS time to Java time (number of milliseconds since epoch). */ public static long dosToJavaTime(long dtime) { - int year; - int month; - int day; + int year = (int) (((dtime >> 25) & 0x7f) + 1980); + int month = (int) ((dtime >> 21) & 0x0f); + int day = (int) ((dtime >> 16) & 0x1f); int hour = (int) ((dtime >> 11) & 0x1f); int minute = (int) ((dtime >> 5) & 0x3f); int second = (int) ((dtime << 1) & 0x3e); - if ((dtime >> 16) == 0) { - // Interpret the 0 DOS date as 1979-11-30 for compatibility with - // other implementations. - year = 1979; - month = 11; - day = 30; - } else { - year = (int) (((dtime >> 25) & 0x7f) + 1980); - month = (int) ((dtime >> 21) & 0x0f); - day = (int) ((dtime >> 16) & 0x1f); + + if (month > 0 && month < 13 && day > 0 && hour < 24 && minute < 60 && second < 60) { + try { + LocalDateTime ldt = LocalDateTime.of(year, month, day, hour, minute, second); + return TimeUnit.MILLISECONDS.convert(ldt.toEpochSecond( + ZoneId.systemDefault().getRules().getOffset(ldt)), TimeUnit.SECONDS); + } catch (DateTimeException dte) { + // ignore + } } - LocalDateTime ldt = LocalDateTime.of(year, month, day, hour, minute, second); - return TimeUnit.MILLISECONDS.convert(ldt.toEpochSecond( - ZoneId.systemDefault().getRules().getOffset(ldt)), TimeUnit.SECONDS); + return overflowDosToJavaTime(year, month, day, hour, minute, second); } + /* + * Deal with corner cases where an arguably mal-formed DOS time is used + */ + @SuppressWarnings("deprecation") // Use of Date constructor + private static long overflowDosToJavaTime(int year, int month, int day, + int hour, int minute, int second) { + return new Date(year - 1900, month - 1, day, hour, minute, second).getTime(); + } + + /** * Converts extended DOS time to Java time, where up to 1999 milliseconds * might be encoded into the upper half of the returned long. diff -r eb010905ccb7 -r 39d1de71faca src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipUtils.java --- a/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipUtils.java Thu Oct 12 13:35:41 2017 +0200 +++ b/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipUtils.java Thu Oct 12 16:00:29 2017 +0200 @@ -27,10 +27,12 @@ import java.io.IOException; import java.io.OutputStream; +import java.time.DateTimeException; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.Arrays; +import java.util.Date; import java.util.regex.PatternSyntaxException; import java.util.concurrent.TimeUnit; @@ -106,26 +108,32 @@ * Converts DOS time to Java time (number of milliseconds since epoch). */ public static long dosToJavaTime(long dtime) { - int year; - int month; - int day; + int year = (int) (((dtime >> 25) & 0x7f) + 1980); + int month = (int) ((dtime >> 21) & 0x0f); + int day = (int) ((dtime >> 16) & 0x1f); int hour = (int) ((dtime >> 11) & 0x1f); int minute = (int) ((dtime >> 5) & 0x3f); int second = (int) ((dtime << 1) & 0x3e); - if ((dtime >> 16) == 0) { - // Interpret the 0 DOS date as 1979-11-30 for compatibility with - // other implementations. - year = 1979; - month = 11; - day = 30; - } else { - year = (int) (((dtime >> 25) & 0x7f) + 1980); - month = (int) ((dtime >> 21) & 0x0f); - day = (int) ((dtime >> 16) & 0x1f); + + if (month > 0 && month < 13 && day > 0 && hour < 24 && minute < 60 && second < 60) { + try { + LocalDateTime ldt = LocalDateTime.of(year, month, day, hour, minute, second); + return TimeUnit.MILLISECONDS.convert(ldt.toEpochSecond( + ZoneId.systemDefault().getRules().getOffset(ldt)), TimeUnit.SECONDS); + } catch (DateTimeException dte) { + // ignore + } } - LocalDateTime ldt = LocalDateTime.of(year, month, day, hour, minute, second); - return TimeUnit.MILLISECONDS.convert(ldt.toEpochSecond( - ZoneId.systemDefault().getRules().getOffset(ldt)), TimeUnit.SECONDS); + return overflowDosToJavaTime(year, month, day, hour, minute, second); + } + + /* + * Deal with corner cases where an arguably mal-formed DOS time is used + */ + @SuppressWarnings("deprecation") // Use of Date constructor + private static long overflowDosToJavaTime(int year, int month, int day, + int hour, int minute, int second) { + return new Date(year - 1900, month - 1, day, hour, minute, second).getTime(); } /* diff -r eb010905ccb7 -r 39d1de71faca test/jdk/java/util/zip/ZipFile/ZeroDate.java --- a/test/jdk/java/util/zip/ZipFile/ZeroDate.java Thu Oct 12 13:35:41 2017 +0200 +++ b/test/jdk/java/util/zip/ZipFile/ZeroDate.java Thu Oct 12 16:00:29 2017 +0200 @@ -34,14 +34,16 @@ import java.nio.file.Path; import java.time.Instant; import java.time.LocalDate; +import java.time.LocalDateTime; import java.time.ZoneId; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; /* @test - * @bug 8184940 + * @bug 8184940 8188869 * @summary JDK 9 rejects zip files where the modified day or month is 0 + * or otherwise represent an invalid date, such as 1980-02-30 24:60:60 * @author Liam Miller-Cushon */ public class ZeroDate { @@ -63,12 +65,19 @@ Files.delete(path); // year, month, day are zero - testDate(data.clone(), 0, LocalDate.of(1979, 11, 30)); + testDate(data.clone(), 0, LocalDate.of(1979, 11, 30).atStartOfDay()); // only year is zero - testDate(data.clone(), 0 << 25 | 4 << 21 | 5 << 16, LocalDate.of(1980, 4, 5)); + testDate(data.clone(), 0 << 25 | 4 << 21 | 5 << 16, LocalDate.of(1980, 4, 5).atStartOfDay()); + // month is greater than 12 + testDate(data.clone(), 0 << 25 | 13 << 21 | 1 << 16, LocalDate.of(1981, 1, 1).atStartOfDay()); + // 30th of February + testDate(data.clone(), 0 << 25 | 2 << 21 | 30 << 16, LocalDate.of(1980, 3, 1).atStartOfDay()); + // 30th of February, 24:60:60 + testDate(data.clone(), 0 << 25 | 2 << 21 | 30 << 16 | 24 << 11 | 60 << 5 | 60 >> 1, + LocalDateTime.of(1980, 3, 2, 1, 1, 0)); } - private static void testDate(byte[] data, int date, LocalDate expected) throws IOException { + private static void testDate(byte[] data, int date, LocalDateTime expected) throws IOException { // set the datetime int endpos = data.length - ENDHDR; int cenpos = u16(data, endpos + ENDOFF); @@ -84,8 +93,7 @@ try (ZipFile zf = new ZipFile(path.toFile())) { ZipEntry ze = zf.entries().nextElement(); Instant actualInstant = ze.getLastModifiedTime().toInstant(); - Instant expectedInstant = - expected.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant(); + Instant expectedInstant = expected.atZone(ZoneId.systemDefault()).toInstant(); if (!actualInstant.equals(expectedInstant)) { throw new AssertionError( String.format("actual: %s, expected: %s", actualInstant, expectedInstant)); diff -r eb010905ccb7 -r 39d1de71faca test/jdk/jdk/nio/zipfs/ZeroDate.java --- a/test/jdk/jdk/nio/zipfs/ZeroDate.java Thu Oct 12 13:35:41 2017 +0200 +++ b/test/jdk/jdk/nio/zipfs/ZeroDate.java Thu Oct 12 16:00:29 2017 +0200 @@ -38,14 +38,16 @@ import java.nio.file.attribute.BasicFileAttributes; import java.time.Instant; import java.time.LocalDate; +import java.time.LocalDateTime; import java.time.ZoneId; import java.util.Collections; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; /* @test - * @bug 8184940 8186227 + * @bug 8184940 8186227 8188869 * @summary JDK 9 rejects zip files where the modified day or month is 0 + * or otherwise represent an invalid date, such as 1980-02-30 24:60:60 * @author Liam Miller-Cushon */ public class ZeroDate { @@ -67,12 +69,19 @@ Files.delete(path); // year, month, day are zero - testDate(data.clone(), 0, LocalDate.of(1979, 11, 30)); + testDate(data.clone(), 0, LocalDate.of(1979, 11, 30).atStartOfDay()); // only year is zero - testDate(data.clone(), 0 << 25 | 4 << 21 | 5 << 16, LocalDate.of(1980, 4, 5)); + testDate(data.clone(), 0 << 25 | 4 << 21 | 5 << 16, LocalDate.of(1980, 4, 5).atStartOfDay()); + // month is greater than 12 + testDate(data.clone(), 0 << 25 | 13 << 21 | 1 << 16, LocalDate.of(1981, 1, 1).atStartOfDay()); + // 30th of February + testDate(data.clone(), 0 << 25 | 2 << 21 | 30 << 16, LocalDate.of(1980, 3, 1).atStartOfDay()); + // 30th of February, 24:60:60 + testDate(data.clone(), 0 << 25 | 2 << 21 | 30 << 16 | 24 << 11 | 60 << 5 | 60 >> 1, + LocalDateTime.of(1980, 3, 2, 1, 1, 0)); } - private static void testDate(byte[] data, int date, LocalDate expected) throws IOException { + private static void testDate(byte[] data, int date, LocalDateTime expected) throws IOException { // set the datetime int endpos = data.length - ENDHDR; int cenpos = u16(data, endpos + ENDOFF); @@ -93,7 +102,7 @@ .lastModifiedTime() .toInstant(); Instant expectedInstant = - expected.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant(); + expected.atZone(ZoneId.systemDefault()).toInstant(); if (!actualInstant.equals(expectedInstant)) { throw new AssertionError( String.format("actual: %s, expected: %s", actualInstant, expectedInstant));