Skip to content
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
21 changes: 13 additions & 8 deletions core/src/main/java/com/alibaba/fastjson2/JSONReaderJSONB.java
Original file line number Diff line number Diff line change
Expand Up @@ -215,15 +215,21 @@ public int readLength() {
return type;
}

private static int getIntByte(byte[] bytes, int offset, int type) {
private int getIntByte(byte[] bytes, int offset, int type) {
if (offset >= end) {
throw outOfBoundsCheckFromToIndex(offset, end);
Comment thread
DarrenChangJR marked this conversation as resolved.
Comment thread
DarrenChangJR marked this conversation as resolved.
}
return ((type - BC_INT32_BYTE_ZERO) << 8) + (bytes[offset] & 0xFF);
}

private static int getInt3(byte[] bytes, int offset, int type) {
return ((type - BC_INT32_SHORT_ZERO) << 16) + (getShortBE(bytes, offset) & 0xFFFF);
}

private static int getLongByte(byte[] bytes, int offset, int type) {
private int getLongByte(byte[] bytes, int offset, int type) {
if (offset >= end) {
throw outOfBoundsCheckFromToIndex(offset, end);
}
return ((type - BC_INT64_BYTE_ZERO) << 8) + (bytes[offset] & 0xFF);
}

Expand Down Expand Up @@ -451,12 +457,12 @@ public Map<String, Object> readObject() {
} else if (isInt32Byte(valueType)) {
value = getIntByte(bytes, offset + 1, valueType);
offset += 2;
} else if (isInt32Short(valueType) && offset + 1 < end) {
} else if (isInt32Short(valueType) && offset + 2 < end) {
int int32Value = getInt3(bytes, offset + 1, valueType);
offset += 3;
value = int32Value;
} else if (valueType == BC_INT32 && offset + 3 < end) {
int int32Value = getIntBE(bytes, offset + 1);
} else if (valueType == BC_INT32) {
int int32Value = getIntBE(bytes, check3(offset + 1, end));
offset += 5;
value = int32Value;
} else {
Expand Down Expand Up @@ -1637,7 +1643,7 @@ public long readTypeHashCode() {
offset++;
typelen = type;
} else {
typelen = ((type - BC_INT32_BYTE_ZERO) << 8) + (bytes[offset + 1] & 0xFF);
typelen = getIntByte(bytes, offset + 1, type);
offset += 2;
}

Expand Down Expand Up @@ -1729,8 +1735,7 @@ public long readTypeHashCode0() {
typeIndex = strtype;
} else if (strtype <= BC_INT32_BYTE_MAX) {
offset++;
typeIndex = ((strtype - BC_INT32_BYTE_ZERO) << 8)
+ (bytes[offset++] & 0xFF);
typeIndex = getIntByte(bytes, offset++, strtype);
} else {
typeIndex = readInt32Value();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package com.alibaba.fastjson2.jsonb;

import com.alibaba.fastjson2.JSONB;
import com.alibaba.fastjson2.JSONException;
import com.alibaba.fastjson2.JSONReader;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.function.Function;

import static com.alibaba.fastjson2.JSONB.Constants.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

@Tag("jsonb")
public class JSONBCompactNumberBoundsTest {
@Test
public void truncatedCompactInt32Byte() {
byte[] bytes = {BC_INT32_BYTE_ZERO};
assertMalformedAcrossNumberReaders(bytes);
assertMalformedSlice(bytes);
}

@Test
public void truncatedCompactInt32Short() {
assertMalformedAcrossNumberReaders(new byte[]{BC_INT32_SHORT_ZERO});
assertMalformedAcrossNumberReaders(new byte[]{BC_INT32_SHORT_ZERO, 0});
}

@Test
public void truncatedCompactInt64Byte() {
byte[] bytes = {BC_INT64_BYTE_ZERO};
assertMalformedAcrossNumberReaders(bytes);
assertMalformedSlice(bytes);
}

@Test
public void truncatedCompactInt64Short() {
assertMalformedAcrossNumberReaders(new byte[]{BC_INT64_SHORT_ZERO});
assertMalformedAcrossNumberReaders(new byte[]{BC_INT64_SHORT_ZERO, 0});
}

@Test
public void truncatedCompactLength() {
assertThrows(JSONException.class, () -> JSONB.parse(new byte[]{BC_ARRAY, BC_INT32_BYTE_ZERO}));
assertThrows(JSONException.class, () -> JSONB.parse(new byte[]{BC_STR_UTF8, BC_INT32_BYTE_ZERO}));
}

@Test
public void truncatedCompactTypeMetadata() {
byte[] typeNameLength = {
BC_TYPED_ANY, BC_STR_ASCII, (byte) (BC_INT32_BYTE_ZERO + 1)
};
assertThrows(JSONException.class, () -> JSONB.parse(typeNameLength));
assertMalformedSlice(typeNameLength);

byte[] typeIndex = {BC_INT32_BYTE_ZERO};
assertMalformed(typeIndex, JSONReader::readTypeHashCode);
assertMalformedSlice(typeIndex, JSONReader::readTypeHashCode);
}

@Test
public void truncatedCompactValuesInContainers() {
assertThrows(JSONException.class, () -> JSONB.parse(new byte[]{
(byte) (BC_ARRAY_FIX_MIN + 1), BC_INT32_BYTE_ZERO
}));
assertThrows(JSONException.class, () -> JSONB.parse(new byte[]{
BC_OBJECT, BC_STR_ASCII_FIX_1, 'a', BC_INT32_BYTE_ZERO
}));

byte[] int32Short = {
BC_OBJECT, BC_STR_ASCII_FIX_1, 'a', BC_INT32_SHORT_ZERO, 0
};
assertThrows(JSONException.class, () -> JSONB.parseObject(int32Short));

byte[] int32 = {
BC_OBJECT, BC_STR_ASCII_FIX_1, 'a', BC_INT32, 0, 0, 0
};
assertThrows(JSONException.class, () -> JSONB.parseObject(int32));

assertMalformedSlice(new byte[]{
BC_OBJECT, BC_STR_ASCII_FIX_1, 'a', BC_INT32_SHORT_ZERO, 0
});
assertMalformedSlice(new byte[]{
BC_OBJECT, BC_STR_ASCII_FIX_1, 'a', BC_INT32, 0, 0, 0
});
}

@Test
public void validCompactNumberBoundaries() {
Number[] values = {
-2048, 2047,
-262144, 262143,
-2048L, 2047L,
-262144L, 262143L
};
for (Number value : values) {
assertEquals(value, JSONB.parse(JSONB.toBytes(value)));
}

assertEquals(1, JSONB.parseObject(new byte[]{
BC_OBJECT, BC_STR_ASCII_FIX_1, 'a', BC_INT32_SHORT_ZERO, 0, 1, BC_OBJECT_END
}).get("a"));
assertEquals(1, JSONB.parseObject(new byte[]{
BC_OBJECT, BC_STR_ASCII_FIX_1, 'a', BC_INT32, 0, 0, 0, 1, BC_OBJECT_END
}).get("a"));
}

private static void assertMalformedAcrossNumberReaders(byte[] bytes) {
assertThrows(JSONException.class, () -> JSONB.parse(bytes));
assertThrows(JSONException.class, () -> JSONB.parseObject(bytes, Integer.class));
assertThrows(JSONException.class, () -> JSONB.parseObject(bytes, Long.class));
assertThrows(JSONException.class, () -> JSONB.parseObject(bytes, BigInteger.class));
assertThrows(JSONException.class, () -> JSONB.parseObject(bytes, BigDecimal.class));

assertMalformed(bytes, JSONReader::readAny);
assertMalformed(bytes, JSONReader::readInt32Value);
assertMalformed(bytes, JSONReader::readInt32);
assertMalformed(bytes, JSONReader::readInt64Value);
assertMalformed(bytes, JSONReader::readInt64);
assertMalformed(bytes, JSONReader::readNumber);
assertMalformed(bytes, JSONReader::readBigInteger);
assertMalformed(bytes, JSONReader::readBigDecimal);
assertMalformed(bytes, JSONReader::readFloatValue);
assertMalformed(bytes, JSONReader::readDoubleValue);
}

private static void assertMalformed(byte[] bytes, Function<JSONReader, ?> action) {
JSONReader reader = JSONReader.ofJSONB(bytes);
assertThrows(JSONException.class, () -> action.apply(reader));
}

private static void assertMalformedSlice(byte[] bytes) {
assertMalformedSlice(bytes, JSONReader::readAny);
}

private static void assertMalformedSlice(byte[] bytes, Function<JSONReader, ?> action) {
byte[] backing = new byte[bytes.length + 2];
System.arraycopy(bytes, 0, backing, 0, bytes.length);
backing[bytes.length] = 1;
backing[bytes.length + 1] = BC_OBJECT_END;
JSONReader reader = JSONReader.ofJSONB(backing, 0, bytes.length);
assertThrows(JSONException.class, () -> action.apply(reader));
}
}