From 55af1b954e923de72720cbf562c3569bd10d9995 Mon Sep 17 00:00:00 2001 From: Torsten Grote <t@grobox.de> Date: Mon, 31 Oct 2016 11:29:41 -0200 Subject: [PATCH] Limit the depth of nested BDF structures --- .../org/briarproject/data/BdfReaderImpl.java | 33 +++++++-- .../briarproject/data/BdfReaderImplTest.java | 67 ++++++++++++++++++- 2 files changed, 93 insertions(+), 7 deletions(-) diff --git a/briar-core/src/org/briarproject/data/BdfReaderImpl.java b/briar-core/src/org/briarproject/data/BdfReaderImpl.java index 9c02a26dd0..59a53cfe63 100644 --- a/briar-core/src/org/briarproject/data/BdfReaderImpl.java +++ b/briar-core/src/org/briarproject/data/BdfReaderImpl.java @@ -8,6 +8,8 @@ import org.briarproject.api.data.BdfReader; import java.io.IOException; import java.io.InputStream; +import javax.annotation.concurrent.NotThreadSafe; + import static org.briarproject.api.data.BdfDictionary.NULL_VALUE; import static org.briarproject.data.Types.DICTIONARY; import static org.briarproject.data.Types.END; @@ -27,9 +29,11 @@ import static org.briarproject.data.Types.STRING_32; import static org.briarproject.data.Types.STRING_8; import static org.briarproject.data.Types.TRUE; -// This class is not thread-safe +@NotThreadSafe class BdfReaderImpl implements BdfReader { + final static int DEFAULT_NESTED_LIMIT = 5; + private static final byte[] EMPTY_BUFFER = new byte[] {}; private final InputStream in; @@ -37,9 +41,16 @@ class BdfReaderImpl implements BdfReader { private boolean hasLookahead = false, eof = false; private byte next; private byte[] buf = new byte[8]; + private final int nestedLimit; BdfReaderImpl(InputStream in) { this.in = in; + this.nestedLimit = DEFAULT_NESTED_LIMIT; + } + + BdfReaderImpl(InputStream in, int nestedLimit) { + this.in = in; + this.nestedLimit = nestedLimit; } private void readLookahead() throws IOException { @@ -77,7 +88,7 @@ class BdfReaderImpl implements BdfReader { } } - private Object readObject() throws IOException { + private Object readObject(int level) throws IOException { if (hasNull()) { readNull(); return NULL_VALUE; @@ -87,8 +98,8 @@ class BdfReaderImpl implements BdfReader { if (hasDouble()) return readDouble(); if (hasString()) return readString(Integer.MAX_VALUE); if (hasRaw()) return readRaw(Integer.MAX_VALUE); - if (hasList()) return readList(); - if (hasDictionary()) return readDictionary(); + if (hasList()) return readList(level); + if (hasDictionary()) return readDictionary(level); throw new FormatException(); } @@ -287,10 +298,15 @@ class BdfReaderImpl implements BdfReader { } public BdfList readList() throws IOException { + return readList(1); + } + + private BdfList readList(int level) throws IOException { if (!hasList()) throw new FormatException(); + if (level > nestedLimit) throw new FormatException(); BdfList list = new BdfList(); readListStart(); - while (!hasListEnd()) list.add(readObject()); + while (!hasListEnd()) list.add(readObject(level + 1)); readListEnd(); return list; } @@ -333,11 +349,16 @@ class BdfReaderImpl implements BdfReader { } public BdfDictionary readDictionary() throws IOException { + return readDictionary(1); + } + + private BdfDictionary readDictionary(int level) throws IOException { if (!hasDictionary()) throw new FormatException(); + if (level > nestedLimit) throw new FormatException(); BdfDictionary dictionary = new BdfDictionary(); readDictionaryStart(); while (!hasDictionaryEnd()) - dictionary.put(readString(Integer.MAX_VALUE), readObject()); + dictionary.put(readString(Integer.MAX_VALUE), readObject(level + 1)); readDictionaryEnd(); return dictionary; } diff --git a/briar-tests/src/org/briarproject/data/BdfReaderImplTest.java b/briar-tests/src/org/briarproject/data/BdfReaderImplTest.java index 0f1cbf1d5c..ae4de10db1 100644 --- a/briar-tests/src/org/briarproject/data/BdfReaderImplTest.java +++ b/briar-tests/src/org/briarproject/data/BdfReaderImplTest.java @@ -11,6 +11,7 @@ import org.junit.Test; import java.io.ByteArrayInputStream; import static org.briarproject.api.data.BdfDictionary.NULL_VALUE; +import static org.briarproject.data.BdfReaderImpl.DEFAULT_NESTED_LIMIT; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -472,9 +473,73 @@ public class BdfReaderImplTest extends BriarTestCase { assertTrue(r.eof()); } + @Test + public void testNestedListWithinDepthLimit() throws Exception { + // A list containing a list containing a list containing a list... + String lists = ""; + for (int i = 1; i <= DEFAULT_NESTED_LIMIT; i++) lists += "60"; + for (int i = 1; i <= DEFAULT_NESTED_LIMIT; i++) lists += "80"; + setContents(lists); + r.readList(); + assertTrue(r.eof()); + } + + @Test(expected = FormatException.class) + public void testNestedListOutsideDepthLimit() throws Exception { + // A list containing a list containing a list containing a list... + String lists = ""; + for (int i = 1; i <= DEFAULT_NESTED_LIMIT + 1; i++) lists += "60"; + for (int i = 1; i <= DEFAULT_NESTED_LIMIT + 1; i++) lists += "80"; + setContents(lists); + r.readList(); + } + + @Test + public void testNestedDictionaryWithinDepthLimit() throws Exception { + // A dictionary containing a dictionary containing a dictionary... + String dicts = ""; + for (int i = 1; i <= DEFAULT_NESTED_LIMIT; i++) + dicts += "70" + "41" + "03" + "666F6F"; + dicts += "11"; + for (int i = 1; i <= DEFAULT_NESTED_LIMIT; i++) + dicts += "80"; + setContents(dicts); + r.readDictionary(); + assertTrue(r.eof()); + } + + @Test(expected = FormatException.class) + public void testNestedDictionaryOutsideDepthLimit() throws Exception { + // A dictionary containing a dictionary containing a dictionary... + String dicts = ""; + for (int i = 1; i <= DEFAULT_NESTED_LIMIT + 1; i++) + dicts += "70" + "41" + "03" + "666F6F"; + dicts += "11"; + for (int i = 1; i <= DEFAULT_NESTED_LIMIT + 1; i++) + dicts += "80"; + setContents(dicts); + r.readDictionary(); + } + + @Test(expected = FormatException.class) + public void testOpenList() throws Exception { + // A list that is not closed + String list = "60"; + setContents(list); + r.readList(); + } + + @Test(expected = FormatException.class) + public void testOpenDictionary() throws Exception { + // A dictionary that is not closed + String dicts = "70" + "41" + "03" + "666F6F"; + setContents(dicts); + r.readDictionary(); + } + private void setContents(String hex) { ByteArrayInputStream in = new ByteArrayInputStream( StringUtils.fromHexString(hex)); - r = new BdfReaderImpl(in); + r = new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT); } } -- GitLab