Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix segsev in WebP #8

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 7 additions & 19 deletions src/javase/java/com/luciad/imageio/webp/WebPWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,25 +83,13 @@ private static byte[] encode(WebPEncoderOptions aOptions, RenderedImage aImage)
throw new NullPointerException("Image may not be null");
}

// This prevents the JVM/GC from attempting to GC (during periods of high load) when it no longer sees any of the
// variables being referred to any further, despite the underlying WebP library directly using them.
// https://bitbucket.org/luciad/webp-imageio/pull-requests/3/prevent-webpencoderoptionss-finalizer/diff
ThreadLocal<WebPEncoderOptions> encoderThreadLocal = new ThreadLocal<>();
try {
encoderThreadLocal.set(aOptions);

byte[] pixels;
boolean encodeAlpha = hasTranslucency(aImage);
if (encodeAlpha) {
byte[] rgbaData = getRGBA(aImage);
pixels = WebP.encodeRGBA(aOptions, rgbaData, aImage.getWidth(), aImage.getHeight(), aImage.getWidth() * 4);
} else {
byte[] rgbData = getRGB(aImage);
pixels = WebP.encodeRGB(aOptions, rgbData, aImage.getWidth(), aImage.getHeight(), aImage.getWidth() * 3);
}
return pixels;
} finally {
encoderThreadLocal.remove();
boolean encodeAlpha = hasTranslucency(aImage);
if (encodeAlpha) {
byte[] rgbaData = getRGBA(aImage);
return WebP.encodeRGBA(aOptions, rgbaData, aImage.getWidth(), aImage.getHeight(), aImage.getWidth() * 4);
} else {
byte[] rgbData = getRGB(aImage);
return WebP.encodeRGB(aOptions, rgbData, aImage.getWidth(), aImage.getHeight(), aImage.getWidth() * 3);
}
}

Expand Down
20 changes: 17 additions & 3 deletions src/main/java/com/luciad/imageio/webp/WebP.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,13 @@ public static int[] decode(WebPDecoderOptions aOptions, byte[] aData, int aOffse
throw new IllegalArgumentException("Offset/length exceeds array size");
}

int[] pixels = decode(aOptions.fPointer, aData, aOffset, aLength, aOut, ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN));
int[] pixels;
try {
pixels = decode(aOptions.fPointer, aData, aOffset, aLength, aOut, ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN));
} finally {
aOptions.objectCanBeFinalized();
}

VP8StatusCode status = VP8StatusCode.getStatusCode(aOut[0]);
switch (status) {
case VP8_STATUS_OK:
Expand Down Expand Up @@ -83,13 +89,21 @@ public static int[] getInfo(byte[] aData, int aOffset, int aLength) throws IOExc
private static native int getInfo(byte[] aData, int aOffset, int aLength, int[] aOut);

public static byte[] encodeRGBA(WebPEncoderOptions aOptions, byte[] aRgbaData, int aWidth, int aHeight, int aStride) {
return encodeRGBA(aOptions.fPointer, aRgbaData, aWidth, aHeight, aStride);
try {
return encodeRGBA(aOptions.fPointer, aRgbaData, aWidth, aHeight, aStride);
} finally {
aOptions.objectCanBeFinalized();
}
}

private static native byte[] encodeRGBA(long aConfig, byte[] aRgbaData, int aWidth, int aHeight, int aStride);

public static byte[] encodeRGB(WebPEncoderOptions aOptions, byte[] aRgbaData, int aWidth, int aHeight, int aStride) {
return encodeRGB(aOptions.fPointer, aRgbaData, aWidth, aHeight, aStride);
try {
return encodeRGB(aOptions.fPointer, aRgbaData, aWidth, aHeight, aStride);
} finally {
aOptions.objectCanBeFinalized();
}
}

private static native byte[] encodeRGB(long aConfig, byte[] aRgbaData, int aWidth, int aHeight, int aStride);
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/com/luciad/imageio/webp/WebPDecoderOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,20 @@ protected void finalize() throws Throwable {
fPointer = 0L;
}

/**
* Method to tell the Java Garbage collector that the object can now be released (and the native method
* is done working with the object).
*
* There is a finalizer in this object which clears the native assigned memory used by the WebP library
* during garbage collection. Without this method being properly called, garbage collection might happen
* before the native procedure has finished executing since the object is only forwarded via JNI by the
* fPointer and the underlying WebP library directly uses the complete object.
*
* This issue only manifests under high load because the time window between the native memory being used
* and the finalizer being called is very small.
*/
public void objectCanBeFinalized() {};

public int getCropHeight() {
return getCropHeight( fPointer );
}
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/com/luciad/imageio/webp/WebPEncoderOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,20 @@ protected void finalize() throws Throwable {
fPointer = 0L;
}

/**
* Method to tell the Java Garbage collector that the object can now be released (and the native method
* is done working with the object).
*
* There is a finalizer in this object which clears the native assigned memory used by the WebP library
* during garbage collection. Without this method being properly called, garbage collection might happen
* before the native procedure has finished executing since the object is only forwarded via JNI by the
* fPointer and the underlying WebP library directly uses the complete object.
*
* This issue only manifests under high load because the time window between the native memory being used
* and the finalizer being called is very small.
*/
public void objectCanBeFinalized() {};

private static native long createConfig();

private static native void deleteConfig( long aPointer );
Expand Down