undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
This commit is contained in:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
358
test/jdk/java/util/zip/ZipFile/TestZipFile.java
Normal file
358
test/jdk/java/util/zip/ZipFile/TestZipFile.java
Normal file
|
|
@ -0,0 +1,358 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8142508 8146431
|
||||
* @modules java.base/java.util.zip:open
|
||||
* @summary Tests various ZipFile apis
|
||||
* @run main/manual TestZipFile
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.zip.CRC32;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
public class TestZipFile {
|
||||
|
||||
private static final Random r = new Random();
|
||||
private static final int N = 50;
|
||||
private static final int NN = 10;
|
||||
private static final int ENUM = 10000;
|
||||
private static final int ESZ = 10000;
|
||||
private static ExecutorService executor = Executors.newFixedThreadPool(20);
|
||||
private static final Set<Path> paths = new HashSet<>();
|
||||
private static final boolean isWindows = System.getProperty("os.name")
|
||||
.startsWith("Windows");
|
||||
|
||||
static void realMain (String[] args) throws Throwable {
|
||||
try {
|
||||
for (int i = 0; i < N; i++) {
|
||||
test(r.nextInt(ENUM), r.nextInt(ESZ), false, true);
|
||||
test(r.nextInt(ENUM), r.nextInt(ESZ), true, true);
|
||||
}
|
||||
executor.shutdown();
|
||||
executor.awaitTermination(10, TimeUnit.MINUTES);
|
||||
executor = Executors.newFixedThreadPool(20);
|
||||
for (int i = 0; i < NN; i++) {
|
||||
test(r.nextInt(ENUM), 100000 + r.nextInt(ESZ), false, true);
|
||||
test(r.nextInt(ENUM), 100000 + r.nextInt(ESZ), true, true);
|
||||
if(!isWindows) {
|
||||
testCachedDelete();
|
||||
testCachedOverwrite();
|
||||
}
|
||||
}
|
||||
test(70000, 1000, false, true); // > 65536 entry number;
|
||||
testDelete(); // OPEN_DELETE
|
||||
|
||||
executor.shutdown();
|
||||
executor.awaitTermination(10, TimeUnit.MINUTES);
|
||||
} finally {
|
||||
for (Path path : paths) {
|
||||
Files.deleteIfExists(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void test(int numEntry, int szMax, boolean addPrefix, boolean cleanOld) {
|
||||
String name = "test-" + r.nextInt() + ".zip";
|
||||
Zip zip = new Zip(name, numEntry, szMax, addPrefix, cleanOld);
|
||||
for (int i = 0; i < NN; i++) {
|
||||
executor.submit(() -> doTest(zip));
|
||||
}
|
||||
}
|
||||
|
||||
// test scenario:
|
||||
// (1) open the ZipFile(zip) with OPEN_READ | OPEN_DELETE
|
||||
// (2) test the ZipFile works correctly
|
||||
// (3) check the zip is deleted after ZipFile gets closed
|
||||
static void testDelete() throws Throwable {
|
||||
String name = "testDelete-" + r.nextInt() + ".zip";
|
||||
Zip zip = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true);
|
||||
try (ZipFile zf = new ZipFile(new File(zip.name),
|
||||
ZipFile.OPEN_READ | ZipFile.OPEN_DELETE ))
|
||||
{
|
||||
doTest0(zip, zf);
|
||||
}
|
||||
Path p = Paths.get(name);
|
||||
if (Files.exists(p)) {
|
||||
fail("Failed to delete " + name + " with OPEN_DELETE");
|
||||
}
|
||||
}
|
||||
|
||||
// test scenario:
|
||||
// (1) keep a ZipFile(zip1) alive (in ZipFile's cache), dont close it
|
||||
// (2) delete zip1 and create zip2 with the same name the zip1 with zip2
|
||||
// (3) zip1 tests should fail, but no crash
|
||||
// (4) zip2 tasks should all get zip2, then pass normal testing.
|
||||
static void testCachedDelete() throws Throwable {
|
||||
String name = "testCachedDelete-" + r.nextInt() + ".zip";
|
||||
Zip zip1 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true);
|
||||
|
||||
try (ZipFile zf = new ZipFile(zip1.name)) {
|
||||
for (int i = 0; i < NN; i++) {
|
||||
executor.submit(() -> verifyNoCrash(zip1));
|
||||
}
|
||||
// delete the "zip1" and create a new one to test
|
||||
Zip zip2 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true);
|
||||
/*
|
||||
System.out.println("========================================");
|
||||
System.out.printf(" zip1=%s, mt=%d, enum=%d%n ->attrs=[key=%s, sz=%d, mt=%d]%n",
|
||||
zip1.name, zip1.lastModified, zip1.entries.size(),
|
||||
zip1.attrs.fileKey(), zip1.attrs.size(), zip1.attrs.lastModifiedTime().toMillis());
|
||||
System.out.printf(" zip2=%s, mt=%d, enum=%d%n ->attrs=[key=%s, sz=%d, mt=%d]%n",
|
||||
zip2.name, zip2.lastModified, zip2.entries.size(),
|
||||
zip2.attrs.fileKey(), zip2.attrs.size(), zip2.attrs.lastModifiedTime().toMillis());
|
||||
*/
|
||||
for (int i = 0; i < NN; i++) {
|
||||
executor.submit(() -> doTest(zip2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// overwrite the "zip1" and create a new one to test. So the two zip files
|
||||
// have the same fileKey, but probably different lastModified()
|
||||
static void testCachedOverwrite() throws Throwable {
|
||||
String name = "testCachedOverWrite-" + r.nextInt() + ".zip";
|
||||
Zip zip1 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true);
|
||||
try (ZipFile zf = new ZipFile(zip1.name)) {
|
||||
for (int i = 0; i < NN; i++) {
|
||||
executor.submit(() -> verifyNoCrash(zip1));
|
||||
}
|
||||
// overwrite the "zip1" with new contents
|
||||
Zip zip2 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, false);
|
||||
for (int i = 0; i < NN; i++) {
|
||||
executor.submit(() -> doTest(zip2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// just check the entries and contents. since the file has been either overwritten
|
||||
// or deleted/rewritten, we only care if it crahes or not.
|
||||
static void verifyNoCrash(Zip zip) throws RuntimeException {
|
||||
try (ZipFile zf = new ZipFile(zip.name)) {
|
||||
List<ZipEntry> zlist = new ArrayList<>(zip.entries.keySet());
|
||||
String[] elist = zf.stream().map(e -> e.getName()).toArray(String[]::new);
|
||||
if (!Arrays.equals(elist,
|
||||
zlist.stream().map( e -> e.getName()).toArray(String[]::new)))
|
||||
{
|
||||
//System.out.printf("++++++ LIST NG [%s] entries.len=%d, expected=%d+++++++%n",
|
||||
// zf.getName(), elist.length, zlist.size());
|
||||
return;
|
||||
}
|
||||
for (ZipEntry ze : zlist) {
|
||||
byte[] zdata = zip.entries.get(ze);
|
||||
ZipEntry e = zf.getEntry(ze.getName());
|
||||
if (e != null) {
|
||||
checkEqual(e, ze);
|
||||
if (!e.isDirectory()) {
|
||||
// check with readAllBytes
|
||||
try (InputStream is = zf.getInputStream(e)) {
|
||||
if (!Arrays.equals(zdata, is.readAllBytes())) {
|
||||
//System.out.printf("++++++ BYTES NG [%s]/[%s] ++++++++%n",
|
||||
// zf.getName(), ze.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
// t.printStackTrace();
|
||||
// fail(t.toString());
|
||||
}
|
||||
}
|
||||
|
||||
static void checkEqual(ZipEntry x, ZipEntry y) {
|
||||
if (x.getName().equals(y.getName()) &&
|
||||
x.isDirectory() == y.isDirectory() &&
|
||||
x.getMethod() == y.getMethod() &&
|
||||
(x.getTime() / 2000) == y.getTime() / 2000 &&
|
||||
x.getSize() == y.getSize() &&
|
||||
x.getCompressedSize() == y.getCompressedSize() &&
|
||||
x.getCrc() == y.getCrc() &&
|
||||
x.getComment().equals(y.getComment())
|
||||
) {
|
||||
pass();
|
||||
} else {
|
||||
fail(x + " not equal to " + y);
|
||||
System.out.printf(" %s %s%n", x.getName(), y.getName());
|
||||
System.out.printf(" %d %d%n", x.getMethod(), y.getMethod());
|
||||
System.out.printf(" %d %d%n", x.getTime(), y.getTime());
|
||||
System.out.printf(" %d %d%n", x.getSize(), y.getSize());
|
||||
System.out.printf(" %d %d%n", x.getCompressedSize(), y.getCompressedSize());
|
||||
System.out.printf(" %d %d%n", x.getCrc(), y.getCrc());
|
||||
System.out.println("-----------------");
|
||||
}
|
||||
}
|
||||
|
||||
static void doTest(Zip zip) throws RuntimeException {
|
||||
//Thread me = Thread.currentThread();
|
||||
try (ZipFile zf = new ZipFile(zip.name)) {
|
||||
doTest0(zip, zf);
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
static void doTest0(Zip zip, ZipFile zf) throws Throwable {
|
||||
// (0) check zero-length entry name, no AIOOBE
|
||||
try {
|
||||
check(zf.getEntry("") == null);
|
||||
} catch (Throwable t) {
|
||||
unexpected(t);
|
||||
}
|
||||
|
||||
List<ZipEntry> list = new ArrayList<>(zip.entries.keySet());
|
||||
// (1) check entry list, in expected order
|
||||
if (!check(Arrays.equals(
|
||||
list.stream().map( e -> e.getName()).toArray(String[]::new),
|
||||
zf.stream().map( e -> e.getName()).toArray(String[]::new)))) {
|
||||
return;
|
||||
}
|
||||
// (2) shuffle, and check each entry and its bytes
|
||||
Collections.shuffle(list);
|
||||
for (ZipEntry ze : list) {
|
||||
byte[] data = zip.entries.get(ze);
|
||||
ZipEntry e = zf.getEntry(ze.getName());
|
||||
checkEqual(e, ze);
|
||||
if (!e.isDirectory()) {
|
||||
// check with readAllBytes
|
||||
try (InputStream is = zf.getInputStream(e)) {
|
||||
check(Arrays.equals(data, is.readAllBytes()));
|
||||
}
|
||||
// check with smaller sized buf
|
||||
try (InputStream is = zf.getInputStream(e)) {
|
||||
byte[] buf = new byte[(int)e.getSize()];
|
||||
int sz = r.nextInt((int)e.getSize()/4 + 1) + 1;
|
||||
int off = 0;
|
||||
int n;
|
||||
while ((n = is.read(buf, off, buf.length - off)) > 0) {
|
||||
off += n;
|
||||
}
|
||||
check(is.read() == -1);
|
||||
check(Arrays.equals(data, buf));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class Zip {
|
||||
final String name;
|
||||
final Map<ZipEntry, byte[]> entries;
|
||||
BasicFileAttributes attrs;
|
||||
long lastModified;
|
||||
|
||||
Zip(String name, int num, int szMax, boolean prefix, boolean clean) {
|
||||
this.name = name;
|
||||
entries = new LinkedHashMap<>(num);
|
||||
try {
|
||||
Path p = Paths.get(name);
|
||||
if (clean) {
|
||||
Files.deleteIfExists(p);
|
||||
}
|
||||
paths.add(p);
|
||||
} catch (Exception x) {
|
||||
throw new RuntimeException(x);
|
||||
}
|
||||
|
||||
try (FileOutputStream fos = new FileOutputStream(name);
|
||||
BufferedOutputStream bos = new BufferedOutputStream(fos);
|
||||
ZipOutputStream zos = new ZipOutputStream(bos))
|
||||
{
|
||||
if (prefix) {
|
||||
byte[] bytes = new byte[r.nextInt(1000)];
|
||||
r.nextBytes(bytes);
|
||||
bos.write(bytes);
|
||||
}
|
||||
CRC32 crc = new CRC32();
|
||||
for (int i = 0; i < num; i++) {
|
||||
String ename = "entry-" + i + "-name-" + r.nextLong();
|
||||
ZipEntry ze = new ZipEntry(ename);
|
||||
int method = r.nextBoolean() ? ZipEntry.STORED : ZipEntry.DEFLATED;
|
||||
writeEntry(zos, crc, ze, method, szMax);
|
||||
}
|
||||
// add some manifest entries
|
||||
for (int i = 0; i < r.nextInt(20); i++) {
|
||||
String meta = "META-INF/" + "entry-" + i + "-metainf-" + r.nextLong();
|
||||
ZipEntry ze = new ZipEntry(meta);
|
||||
writeEntry(zos, crc, ze, ZipEntry.STORED, szMax);
|
||||
}
|
||||
} catch (Exception x) {
|
||||
throw new RuntimeException(x);
|
||||
}
|
||||
try {
|
||||
this.attrs = Files.readAttributes(Paths.get(name), BasicFileAttributes.class);
|
||||
this.lastModified = new File(name).lastModified();
|
||||
} catch (Exception x) {
|
||||
throw new RuntimeException(x);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeEntry(ZipOutputStream zos, CRC32 crc,
|
||||
ZipEntry ze, int method, int szMax)
|
||||
throws IOException
|
||||
{
|
||||
ze.setMethod(method);
|
||||
byte[] data = new byte[r.nextInt(szMax + 1)];
|
||||
r.nextBytes(data);
|
||||
if (method == ZipEntry.STORED) { // must set size/csize/crc
|
||||
ze.setSize(data.length);
|
||||
ze.setCompressedSize(data.length);
|
||||
crc.reset();
|
||||
crc.update(data);
|
||||
ze.setCrc(crc.getValue());
|
||||
}
|
||||
ze.setTime(System.currentTimeMillis());
|
||||
ze.setComment(ze.getName());
|
||||
zos.putNextEntry(ze);
|
||||
zos.write(data);
|
||||
zos.closeEntry();
|
||||
entries.put(ze, data);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
static volatile int passed = 0, failed = 0;
|
||||
static void pass() {passed++;}
|
||||
static void pass(String msg) {System.out.println(msg); passed++;}
|
||||
static void fail() {failed++; Thread.dumpStack();}
|
||||
static void fail(String msg) {System.out.println(msg); fail();}
|
||||
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
|
||||
static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;}
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {realMain(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.println("\nPassed = " + passed + " failed = " + failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue