Skip to content
Merged
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
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
CHANGELOG
=========

4.1.1
------------------
Comment thread
coderabbitai[bot] marked this conversation as resolved.

* Fixed decoding of data pointers with offsets of 2 GiB or greater. The
pointer payload was decoded into an `int`, so such offsets were
sign-extended to a negative value and rejected by `Buffer.position()`
with an `IllegalArgumentException`. Every record past the 2 GiB
boundary was unreachable in databases larger than 2 GiB, which have
been supported since 4.0.0.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

4.1.0 (2026-05-12)
------------------

Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/maxmind/db/Decoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ private <T> DecodedValue decode(Class<T> cls, java.lang.reflect.Type genericType
if (type.equals(Type.POINTER)) {
var pointerSize = ((ctrlByte >>> 3) & 0x3) + 1;
var base = pointerSize == 4 ? (byte) 0 : (byte) (ctrlByte & 0x7);
var packed = this.decodeInteger(base, pointerSize);
var packed = Decoder.decodeLong(this.buffer, base, pointerSize);
var pointer = packed + this.pointerBase + POINTER_VALUE_OFFSETS[pointerSize];

return decodePointer(pointer, cls, genericType);
Expand Down
20 changes: 20 additions & 0 deletions src/test/java/com/maxmind/db/PointerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
import com.maxmind.db.Reader.FileMode;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import org.junit.jupiter.api.Test;

public class PointerTest {
Expand Down Expand Up @@ -41,4 +43,22 @@ public void testWithPointers() throws IOException {
map.put("long_key2", "long_value2");
assertEquals(map, decoder.decode(57, Map.class));
}

@SuppressWarnings("static-method")
@Test
public void testPointerBeyond2GiBIsNotSignExtended() throws IOException {
// Control byte for a four-byte pointer, followed by the offset 3473557240.
var buffer = new SingleBuffer(ByteBuffer.wrap(new byte[]{
(byte) 0x38, (byte) 0xCF, (byte) 0x0A, (byte) 0x46, (byte) 0xF8}));

var observed = new AtomicLong(Long.MIN_VALUE);
NodeCache cache = (key, loader) -> {
observed.set(key.offset());
return new DecodedValue(null);
};

new Decoder(cache, buffer, 0).decode(0, Object.class);

assertEquals(3473557240L, observed.get());
}
}
Loading