Skip to content

Commit

Permalink
Fix jarinfer cli output determinism (#884)
Browse files Browse the repository at this point in the history
When invoking CLI, jar output sha is not constant. This breaks
cacheability in build systems like Buck and Bazel.
Binary investigation (see attached screenshot) of these jars show byte
0xa-0xd in PkZip header is what is changes, and corresponds to file
modified date/time (see
https://users.cs.jmu.edu/buchhofp/forensics/formats/pkzip.html).
This PR is setting these times to 0 explicilty, so that jar sha remains
constant between invocation.


---------

Co-authored-by: Manu Sridharan <msridhar@gmail.com>
  • Loading branch information
oliviernotteghem and msridhar committed Dec 21, 2023
1 parent 10761d9 commit ce41599
Showing 1 changed file with 16 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,18 @@ public static void annotateBytecodeInClass(
annotateBytecode(is, os, nonnullParams, nullableReturns, javaxNullableDesc, javaxNonnullDesc);
}

/**
* Create a zip entry with creation time of 0 to ensure that jars always have the same checksum.
*
* @param name of the zip entry.
* @return the zip entry.
*/
private static ZipEntry createZipEntry(String name) {
ZipEntry entry = new ZipEntry(name);
entry.setTime(0);
return entry;
}

private static void copyAndAnnotateJarEntry(
JarEntry jarEntry,
InputStream is,
Expand All @@ -224,7 +236,7 @@ private static void copyAndAnnotateJarEntry(
throws IOException {
String entryName = jarEntry.getName();
if (entryName.endsWith(".class")) {
jarOS.putNextEntry(new ZipEntry(jarEntry.getName()));
jarOS.putNextEntry(createZipEntry(jarEntry.getName()));
annotateBytecode(is, jarOS, nonnullParams, nullableReturns, nullableDesc, nonnullDesc);
} else if (entryName.equals("META-INF/MANIFEST.MF")) {
// Read full file
Expand All @@ -241,7 +253,7 @@ private static void copyAndAnnotateJarEntry(
if (!manifestText.equals(manifestMinusDigests) && !stripJarSignatures) {
throw new SignedJarException(SIGNED_JAR_ERROR_MESSAGE);
}
jarOS.putNextEntry(new ZipEntry(jarEntry.getName()));
jarOS.putNextEntry(createZipEntry(jarEntry.getName()));
jarOS.write(manifestMinusDigests.getBytes(UTF_8));
} else if (entryName.startsWith("META-INF/")
&& (entryName.endsWith(".DSA")
Expand All @@ -251,7 +263,7 @@ private static void copyAndAnnotateJarEntry(
throw new SignedJarException(SIGNED_JAR_ERROR_MESSAGE);
} // the case where stripJarSignatures==true is handled by default by skipping these files
} else {
jarOS.putNextEntry(new ZipEntry(jarEntry.getName()));
jarOS.putNextEntry(createZipEntry(jarEntry.getName()));
jarOS.write(IOUtils.toByteArray(is));
}
jarOS.closeEntry();
Expand Down Expand Up @@ -329,7 +341,7 @@ public static void annotateBytecodeInAar(
while (zipIterator.hasNext()) {
ZipEntry zipEntry = zipIterator.next();
InputStream is = inputZip.getInputStream(zipEntry);
zipOS.putNextEntry(new ZipEntry(zipEntry.getName()));
zipOS.putNextEntry(createZipEntry(zipEntry.getName()));
if (zipEntry.getName().equals("classes.jar")) {
JarInputStream jarIS = new JarInputStream(is);
JarEntry inputJarEntry = jarIS.getNextJarEntry();
Expand Down

0 comments on commit ce41599

Please sign in to comment.