findNextPositivePowerOfTwo out of bounds

Motivation:
Some usages of findNextPositivePowerOfTwo assume that bounds checking is taken care of by this method. However bounds checking is not taken care of by findNextPositivePowerOfTwo and instead assert statements are used to imply the caller has checked the bounds. This can lead to unexpected non power of 2 return values if the caller is not careful and thus invalidate any logic which depends upon a power of 2.

Modifications:
- Add a safeFindNextPositivePowerOfTwo method which will do runtime bounds checks and always return a power of 2

Result:
Fixes https://github.com/netty/netty/issues/5601
This commit is contained in:
Scott Mitchell 2016-07-29 09:09:29 -07:00
parent 68ee85f1ae
commit 4fb7a526c7
5 changed files with 24 additions and 16 deletions

View File

@ -369,7 +369,7 @@ final class PoolThreadCache {
private int allocations;
MemoryRegionCache(int size, SizeClass sizeClass) {
this.size = MathUtil.findNextPositivePowerOfTwo(size);
this.size = MathUtil.safeFindNextPositivePowerOfTwo(size);
queue = PlatformDependent.newFixedMpscQueue(this.size);
this.sizeClass = sizeClass;
}

View File

@ -27,7 +27,7 @@ import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import static io.netty.util.internal.MathUtil.findNextPositivePowerOfTwo;
import static io.netty.util.internal.MathUtil.safeFindNextPositivePowerOfTwo;
import static java.lang.Math.max;
import static java.lang.Math.min;
@ -66,14 +66,13 @@ public abstract class Recycler<T> {
SystemPropertyUtil.getInt("io.netty.recycler.maxSharedCapacityFactor",
2));
LINK_CAPACITY = findNextPositivePowerOfTwo(
LINK_CAPACITY = safeFindNextPositivePowerOfTwo(
max(SystemPropertyUtil.getInt("io.netty.recycler.linkCapacity", 16), 16));
// By default we allow one push to a Recycler for each 8th try on handles that were never recycled before.
// This should help to slowly increase the capacity of the recycler while not be too sensitive to allocation
// bursts.
RATIO = min(findNextPositivePowerOfTwo(
max(SystemPropertyUtil.getInt("io.netty.recycler.ratio", 8), 2)), 0x40000000);
RATIO = safeFindNextPositivePowerOfTwo(SystemPropertyUtil.getInt("io.netty.recycler.ratio", 8));
if (logger.isDebugEnabled()) {
if (DEFAULT_MAX_CAPACITY == 0) {
@ -116,10 +115,7 @@ public abstract class Recycler<T> {
}
protected Recycler(int maxCapacity, int maxSharedCapacityFactor, int ratio) {
if (ratio > 0x40000000) {
throw new IllegalArgumentException(ratio + ": " + ratio + " (expected: < 0x40000000)");
}
ratioMask = findNextPositivePowerOfTwo(ratio) - 1;
ratioMask = safeFindNextPositivePowerOfTwo(ratio) - 1;
if (maxCapacity <= 0) {
this.maxCapacity = 0;
this.maxSharedCapacityFactor = 1;

View File

@ -193,15 +193,12 @@ public class ResourceLeakDetector<T> {
if (resourceType == null) {
throw new NullPointerException("resourceType");
}
if (samplingInterval <= 0) {
throw new IllegalArgumentException("samplingInterval: " + samplingInterval + " (expected: 1+)");
}
if (maxActive <= 0) {
throw new IllegalArgumentException("maxActive: " + maxActive + " (expected: 1+)");
}
this.resourceType = resourceType;
this.samplingInterval = MathUtil.findNextPositivePowerOfTwo(samplingInterval);
this.samplingInterval = MathUtil.safeFindNextPositivePowerOfTwo(samplingInterval);
// samplingInterval is a power of two so we calculate a mask that we can use to
// check if we need to do any leak detection or not.
mask = this.samplingInterval - 1;

View File

@ -67,6 +67,7 @@ public class IntObjectHashMap<V> implements IntObjectMap<V>, Iterable<IntObjectM
if (initialCapacity < 1) {
throw new IllegalArgumentException("initialCapacity must be >= 1");
}
if (loadFactor <= 0.0f || loadFactor > 1.0f) {
// Cannot exceed 1 because we can never store more than capacity elements;
// using a bigger loadFactor would trigger rehashing before the desired load is reached.

View File

@ -24,9 +24,7 @@ public final class MathUtil {
/**
* Fast method of finding the next power of 2 greater than or equal to the supplied value.
*
* If the value is {@code <= 0} then 1 will be returned.
*
* <p>If the value is {@code <= 0} then 1 will be returned.
* This method is not suitable for {@link Integer#MIN_VALUE} or numbers greater than 2^30.
*
* @param value from which to search for next power of 2
@ -37,6 +35,22 @@ public final class MathUtil {
return 1 << (32 - Integer.numberOfLeadingZeros(value - 1));
}
/**
* Fast method of finding the next power of 2 greater than or equal to the supplied value.
* <p>This method will do runtime bounds checking and call {@link #findNextPositivePowerOfTwo(int)} if within a
* valid range.
* @param value from which to search for next power of 2
* @return The next power of 2 or the value itself if it is a power of 2.
* <p>Special cases for return values are as follows:
* <ul>
* <li>{@code <= 0} -> 1</li>
* <li>{@code >= 2^30} -> 2^30</li>
* </ul>
*/
public static int safeFindNextPositivePowerOfTwo(final int value) {
return value <= 0 ? 1 : value >= 0x40000000 ? 0x40000000 : findNextPositivePowerOfTwo(value);
}
/**
* Determine if the requested {@code index} and {@code length} will fit within {@code capacity}.
* @param index The starting index.