ning/compress — Issue #13: UnsafeChunkDecoder in ning-compress-0.9.0 leads to SIGSEV in gc
Author: whoschek
Created: 2011-11-12 20:26:53+00:00
State: open

Hi Tatu,

I ran across this bug here: Use of UnsafeChunkDecoder in ning-compress-0.9.0 leads to SIGSEV in gc. Workaround: use VanillaChunkDecoder.

[junit] Testsuite: TestLZFOnFiles
[junit] #
[junit] # A fatal error has been detected by the Java Runtime Environment:
[junit] #
[junit] # SIGSEGV (0xb) at pc=0x00007f1a73258829, pid=18898, tid=139751499331328
[junit] #
[junit] # JRE version: 6.0_29-b11
[junit] # Java VM: Java HotSpot(TM) 64-Bit Server VM (20.4-b02 mixed mode linux-amd64 compressed oops)
[junit] # Problematic frame:
[junit] # V [libjvm.so+0x760829] PSPromotionManager::copy_to_survivor_space(oopDesc*)+0x89
[junit] #
[junit] # An error report file with more information is saved as:
[junit] # /export/skytide/version2-main/hs_err_pid18898.log
[junit] #
[junit] # If you would like to submit a bug report, please visit:
[junit] # http://java.sun.com/webapps/bugreport/crash.jsp
[junit] #
[junit] Tests FAILED (crashed)

Reproducible always with ning-compress-0.9.0 on the following platforms:
OracleJava-1.7.0u1 on Ubuntu (server VM)
OracleJava-1.6.0_29 on Ubuntu (server VM)
OracleJava-1.6.0_26 on OSX 10.6.8 (server VM)

Note that the test passes with the VanillaChunkDecoder (to test this I had to add a system property "com.ning.compress.lzf.ChunkDecoderFactory.disableUnsafe" to ChunkDecoderFactory).

Testcase is attached below. I can't figure out how to attach the test data files to this github issue tracker system. I could send them via email, though.

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;

import junit.framework.TestCase;

import com.ning.compress.lzf.LZFInputStream;
import com.ning.compress.lzf.LZFOutputStream;

public class TestLZFOnFiles extends TestCase {

public TestLZFOnFiles(String name) {
super(name);
}

public void testLZFCompressionOnTestFiles() throws Throwable {
for (int i = 0; i < 1000; i++) {
log("iteration: " + i);
testLZFCompressionOnDir(new File("shakespeare"));
}
}

private void testLZFCompressionOnDir(File dir) throws Throwable {
File[] files = dir.listFiles();
for (int i = 0; i < files.length; i++) {
File file = files[i];
if (!file.isDirectory()) {

testLZFCompressionOnFile(file);
} else {
testLZFCompressionOnDir(file);
}
}
}

private void testLZFCompressionOnFile(File file) throws Throwable {
// compress
log(" compressing file " + file);
// File compressedFile = createEmptyFile("test.lzf");
File compressedFile = new File("/tmp/test.lzf");
InputStream in = new BufferedInputStream(new FileInputStream(file));
OutputStream out = new LZFOutputStream(new BufferedOutputStream(
new FileOutputStream(compressedFile)));
byte[] buf = new byte[64 * 1024];
int len;
while ((len = in.read(buf, 0, buf.length)) >= 0) {
out.write(buf, 0, len);
}
in.close();
out.close();

// decompress and verify bytes haven't changed
log("decompressing and verifying file " + file);
in = new BufferedInputStream(new FileInputStream(file));
DataInputStream compressedIn = new DataInputStream(new LZFInputStream(
new BufferedInputStream(new FileInputStream(compressedFile))));
while ((len = in.read(buf, 0, buf.length)) >= 0) {
byte[] buf2 = new byte[len];
compressedIn.readFully(buf2, 0, len);

byte[] trimmedBuf = new byte[len];
System.arraycopy(buf, 0, trimmedBuf, 0, len);

assertEquals(trimmedBuf, buf2);
}
assertEquals(-1, compressedIn.read());
in.close();
compressedIn.close();
}

private void log(String msg) throws IOException
{
System.out.println(msg);
writeFile(new File("/tmp/log"), msg + "\n", true);
}

private static void writeFile(File file, String fileData, boolean append)
throws IOException {
if (!file.getParentFile().exists()) {
file.getParentFile().mkdir();
}

Writer w = new OutputStreamWriter(new FileOutputStream(file, append),
"UTF-8");
try {
w.write(fileData);
} finally {
w.close();
}
}

private void assertEquals(byte[] expected, byte[] actual) {
if (expected == null && actual != null) {
fail();
}
if (expected != null && actual == null) {
fail();
}
if (expected.length != actual.length) {
fail();
}
for (int i = 0; i < expected.length; i++) {
if (expected[i] != actual[i]) {
fail();
}
}
}

}