Skip to content

Commit

Permalink
Use StreamConstraintsException exception in canonicalizer (#948)
Browse files Browse the repository at this point in the history
  • Loading branch information
pjfanning committed Mar 14, 2023
1 parent 7fe79e9 commit 25313f1
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 62 deletions.
Expand Up @@ -5,6 +5,7 @@

import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.base.ParserBase;
import com.fasterxml.jackson.core.exc.StreamConstraintsException;
import com.fasterxml.jackson.core.io.CharTypes;
import com.fasterxml.jackson.core.io.IOContext;
import com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer;
Expand Down Expand Up @@ -38,8 +39,6 @@
public class UTF8DataInputJsonParser
extends ParserBase
{
final static byte BYTE_LF = (byte) '\n';

@SuppressWarnings("deprecation")
private final static int FEAT_MASK_TRAILING_COMMA = Feature.ALLOW_TRAILING_COMMA.getMask();
@SuppressWarnings("deprecation")
Expand Down Expand Up @@ -1768,7 +1767,8 @@ protected String _parseAposName() throws IOException
/**********************************************************
*/

private final String findName(int q1, int lastQuadBytes) throws JsonParseException
private final String findName(int q1, int lastQuadBytes)
throws JsonParseException, StreamConstraintsException
{
q1 = pad(q1, lastQuadBytes);
// Usually we'll find it from the canonical symbol table already
Expand All @@ -1781,7 +1781,8 @@ private final String findName(int q1, int lastQuadBytes) throws JsonParseExcepti
return addName(_quadBuffer, 1, lastQuadBytes);
}

private final String findName(int q1, int q2, int lastQuadBytes) throws JsonParseException
private final String findName(int q1, int q2, int lastQuadBytes)
throws JsonParseException, StreamConstraintsException
{
q2 = pad(q2, lastQuadBytes);
// Usually we'll find it from the canonical symbol table already
Expand All @@ -1795,7 +1796,8 @@ private final String findName(int q1, int q2, int lastQuadBytes) throws JsonPars
return addName(_quadBuffer, 2, lastQuadBytes);
}

private final String findName(int q1, int q2, int q3, int lastQuadBytes) throws JsonParseException
private final String findName(int q1, int q2, int q3, int lastQuadBytes)
throws JsonParseException, StreamConstraintsException
{
q3 = pad(q3, lastQuadBytes);
String name = _symbols.findName(q1, q2, q3);
Expand All @@ -1809,7 +1811,8 @@ private final String findName(int q1, int q2, int q3, int lastQuadBytes) throws
return addName(quads, 3, lastQuadBytes);
}

private final String findName(int[] quads, int qlen, int lastQuad, int lastQuadBytes) throws JsonParseException
private final String findName(int[] quads, int qlen, int lastQuad, int lastQuadBytes)
throws JsonParseException, StreamConstraintsException
{
if (qlen >= quads.length) {
_quadBuffer = quads = _growArrayBy(quads, quads.length);
Expand All @@ -1828,7 +1831,8 @@ private final String findName(int[] quads, int qlen, int lastQuad, int lastQuadB
* multi-byte chars (if any), and then construct Name instance
* and add it to the symbol table.
*/
private final String addName(int[] quads, int qlen, int lastQuadBytes) throws JsonParseException
private final String addName(int[] quads, int qlen, int lastQuadBytes)
throws JsonParseException, StreamConstraintsException
{
/* Ok: must decode UTF-8 chars. No other validation is
* needed, since unescaping has been done earlier as necessary
Expand Down
Expand Up @@ -4,6 +4,7 @@

import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.base.ParserBase;
import com.fasterxml.jackson.core.exc.StreamConstraintsException;
import com.fasterxml.jackson.core.io.CharTypes;
import com.fasterxml.jackson.core.io.IOContext;
import com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer;
Expand Down Expand Up @@ -2309,7 +2310,8 @@ protected String _parseAposName() throws IOException
/**********************************************************
*/

private final String findName(int q1, int lastQuadBytes) throws JsonParseException
private final String findName(int q1, int lastQuadBytes)
throws JsonParseException, StreamConstraintsException
{
q1 = _padLastQuad(q1, lastQuadBytes);
// Usually we'll find it from the canonical symbol table already
Expand All @@ -2322,7 +2324,8 @@ private final String findName(int q1, int lastQuadBytes) throws JsonParseExcepti
return addName(_quadBuffer, 1, lastQuadBytes);
}

private final String findName(int q1, int q2, int lastQuadBytes) throws JsonParseException
private final String findName(int q1, int q2, int lastQuadBytes)
throws JsonParseException, StreamConstraintsException
{
q2 = _padLastQuad(q2, lastQuadBytes);
// Usually we'll find it from the canonical symbol table already
Expand All @@ -2336,7 +2339,8 @@ private final String findName(int q1, int q2, int lastQuadBytes) throws JsonPars
return addName(_quadBuffer, 2, lastQuadBytes);
}

private final String findName(int q1, int q2, int q3, int lastQuadBytes) throws JsonParseException
private final String findName(int q1, int q2, int q3, int lastQuadBytes)
throws JsonParseException, StreamConstraintsException
{
q3 = _padLastQuad(q3, lastQuadBytes);
String name = _symbols.findName(q1, q2, q3);
Expand All @@ -2350,7 +2354,8 @@ private final String findName(int q1, int q2, int q3, int lastQuadBytes) throws
return addName(quads, 3, lastQuadBytes);
}

private final String findName(int[] quads, int qlen, int lastQuad, int lastQuadBytes) throws JsonParseException
private final String findName(int[] quads, int qlen, int lastQuad, int lastQuadBytes)
throws JsonParseException, StreamConstraintsException
{
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
Expand All @@ -2368,7 +2373,8 @@ private final String findName(int[] quads, int qlen, int lastQuad, int lastQuadB
* multi-byte chars (if any), and then construct Name instance
* and add it to the symbol table.
*/
private final String addName(int[] quads, int qlen, int lastQuadBytes) throws JsonParseException
private final String addName(int[] quads, int qlen, int lastQuadBytes)
throws JsonParseException, StreamConstraintsException
{
/* Ok: must decode UTF-8 chars. No other validation is
* needed, since unescaping has been done earlier as necessary
Expand Down
Expand Up @@ -4,6 +4,7 @@

import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.base.ParserBase;
import com.fasterxml.jackson.core.exc.StreamConstraintsException;
import com.fasterxml.jackson.core.io.IOContext;
import com.fasterxml.jackson.core.json.JsonReadContext;
import com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer;
Expand Down Expand Up @@ -639,7 +640,8 @@ protected final JsonToken _closeObjectScope() throws IOException
/**********************************************************
*/

protected final String _findName(int q1, int lastQuadBytes) throws JsonParseException
protected final String _findName(int q1, int lastQuadBytes)
throws JsonParseException, StreamConstraintsException
{
q1 = _padLastQuad(q1, lastQuadBytes);
// Usually we'll find it from the canonical symbol table already
Expand All @@ -652,7 +654,8 @@ protected final String _findName(int q1, int lastQuadBytes) throws JsonParseExce
return _addName(_quadBuffer, 1, lastQuadBytes);
}

protected final String _findName(int q1, int q2, int lastQuadBytes) throws JsonParseException
protected final String _findName(int q1, int q2, int lastQuadBytes)
throws JsonParseException, StreamConstraintsException
{
q2 = _padLastQuad(q2, lastQuadBytes);
// Usually we'll find it from the canonical symbol table already
Expand All @@ -666,7 +669,8 @@ protected final String _findName(int q1, int q2, int lastQuadBytes) throws JsonP
return _addName(_quadBuffer, 2, lastQuadBytes);
}

protected final String _findName(int q1, int q2, int q3, int lastQuadBytes) throws JsonParseException
protected final String _findName(int q1, int q2, int q3, int lastQuadBytes)
throws JsonParseException, StreamConstraintsException
{
q3 = _padLastQuad(q3, lastQuadBytes);
String name = _symbols.findName(q1, q2, q3);
Expand All @@ -684,10 +688,11 @@ protected final String _findName(int q1, int q2, int q3, int lastQuadBytes) thro
// table miss. It needs to demultiplex individual bytes, decode
// multi-byte chars (if any), and then construct Name instance
// and add it to the symbol table.
protected final String _addName(int[] quads, int qlen, int lastQuadBytes) throws JsonParseException
protected final String _addName(int[] quads, int qlen, int lastQuadBytes)
throws JsonParseException, StreamConstraintsException
{
/* Ok: must decode UTF-8 chars. No other validation is
* needed, since unescaping has been done earlier as necessary
* needed, since unescaping has been done earlier, as necessary
* (as well as error reporting for unescaped control chars)
*/
// 4 bytes per quad, except last one maybe less
Expand Down
Expand Up @@ -4,6 +4,7 @@
import java.util.concurrent.atomic.AtomicReference;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.exc.StreamConstraintsException;
import com.fasterxml.jackson.core.util.InternCache;

/**
Expand Down Expand Up @@ -863,7 +864,13 @@ private boolean _verifyLongName2(int[] q, int qlen, int spillOffset)
/**********************************************************
*/

public String addName(String name, int q1) {
/**
* @param name
* @param q1
* @return name (possibly interned)
* @throws StreamConstraintsException if the constraint exceptions
*/
public String addName(String name, int q1) throws StreamConstraintsException {
_verifySharing();
if (_intern) {
name = InternCache.instance.intern(name);
Expand All @@ -876,7 +883,14 @@ public String addName(String name, int q1) {
return name;
}

public String addName(String name, int q1, int q2) {
/**
* @param name
* @param q1
* @param q2
* @return name (possibly interned)
* @throws StreamConstraintsException if the constraint exceptions
*/
public String addName(String name, int q1, int q2) throws StreamConstraintsException {
_verifySharing();
if (_intern) {
name = InternCache.instance.intern(name);
Expand All @@ -896,7 +910,15 @@ public String addName(String name, int q1, int q2) {
return name;
}

public String addName(String name, int q1, int q2, int q3) {
/**
* @param name
* @param q1
* @param q2
* @param q3
* @return name (possibly interned)
* @throws StreamConstraintsException if the constraint exceptions
*/
public String addName(String name, int q1, int q2, int q3) throws StreamConstraintsException {
_verifySharing();
if (_intern) {
name = InternCache.instance.intern(name);
Expand All @@ -911,7 +933,14 @@ public String addName(String name, int q1, int q2, int q3) {
return name;
}

public String addName(String name, int[] q, int qlen)
/**
* @param name
* @param q
* @param qlen
* @return name (possibly interned)
* @throws StreamConstraintsException if the constraint exceptions
*/
public String addName(String name, int[] q, int qlen) throws StreamConstraintsException
{
_verifySharing();
if (_intern) {
Expand All @@ -921,7 +950,7 @@ public String addName(String name, int[] q, int qlen)

switch (qlen) {
case 1:
{
{
offset = _findOffsetForAdd(calcHash(q[0]));
_hashArea[offset] = q[0];
_hashArea[offset+3] = 1;
Expand Down Expand Up @@ -961,16 +990,16 @@ public String addName(String name, int[] q, int qlen)
return name;
}

private void _verifySharing()
private void _verifySharing() throws StreamConstraintsException
{
if (_hashShared) {
// 12-Mar-2021, tatu: prevent modifying of "placeholder" and
// parent tables
if (_parent == null) {
if (_count == 0) { // root
throw new IllegalStateException("Cannot add names to Root symbol table");
throw new StreamConstraintsException("Cannot add names to Root symbol table");
}
throw new IllegalStateException("Cannot add names to Placeholder symbol table");
throw new StreamConstraintsException("Cannot add names to Placeholder symbol table");
}

_hashArea = Arrays.copyOf(_hashArea, _hashArea.length);
Expand All @@ -982,7 +1011,7 @@ private void _verifySharing()
/**
* Method called to find the location within hash table to add a new symbol in.
*/
private int _findOffsetForAdd(int hash)
private int _findOffsetForAdd(int hash) throws StreamConstraintsException
{
// first, check the primary: if slot found, no need for resize
int offset = _calcOffset(hash);
Expand Down Expand Up @@ -1037,7 +1066,7 @@ private int _findOffsetForAdd(int hash)
}

// @since 2.10
private int _resizeAndFindOffsetForAdd(int hash)
private int _resizeAndFindOffsetForAdd(int hash) throws StreamConstraintsException
{
// First things first: we need to resize+rehash (or, if too big, nuke contents)
rehash();
Expand Down Expand Up @@ -1165,10 +1194,16 @@ public int calcHash(int q1, int q2, int q3)
return hash;
}

/**
* @param q int array
* @param qlen length
* @return hash
* @throws IllegalArgumentException if <code>qlen</code> is less than 4
*/
public int calcHash(int[] q, int qlen)
{
if (qlen < 4) {
throw new IllegalArgumentException();
throw new IllegalArgumentException("qlen is too short, needs to be at least 4");
}
/* And then change handling again for "multi-quad" case; mostly
* to make calculation of collisions less fun. For example,
Expand Down Expand Up @@ -1202,7 +1237,7 @@ public int calcHash(int[] q, int qlen)
/**********************************************************
*/

private void rehash()
private void rehash() throws StreamConstraintsException
{
// Note: since we'll make copies, no need to unshare, can just mark as such:
_hashShared = false;
Expand Down Expand Up @@ -1279,7 +1314,7 @@ private void rehash()
// Sanity checks: since corruption difficult to detect, assert explicitly
// with production code
if (copyCount != oldCount) {
throw new IllegalStateException("Failed rehash(): old count="+oldCount+", copyCount="+copyCount);
throw new StreamConstraintsException("Failed rehash(): old count="+oldCount+", copyCount="+copyCount);
}
}

Expand Down Expand Up @@ -1315,13 +1350,13 @@ private final int _spilloverStart() {
return (offset << 3) - offset;
}

protected void _reportTooManyCollisions()
protected void _reportTooManyCollisions() throws StreamConstraintsException
{
// First: do not fuzz about small symbol tables; may get balanced by doubling up
if (_hashSize <= 1024) { // would have spill-over area of 128 entries
return;
}
throw new IllegalStateException("Spill-over slots in symbol table with "+_count
throw new StreamConstraintsException("Spill-over slots in symbol table with "+_count
+" entries, hash area of "+_hashSize+" slots is now full (all "
+(_hashSize >> 3)+" slots -- suspect a DoS attack based on hash collisions."
+" You can disable the check via `JsonFactory.Feature.FAIL_ON_SYMBOL_HASH_OVERFLOW`");
Expand Down

0 comments on commit 25313f1

Please sign in to comment.