exclude: avoid segfault when decoding empty Exclude element

refs #1970

Change-Id: Ia262a23dd7b0ce97986bbdb78baa99539fecd3c7
diff --git a/src/exclude.cpp b/src/exclude.cpp
index 7f069eb..202ab91 100644
--- a/src/exclude.cpp
+++ b/src/exclude.cpp
@@ -21,12 +21,14 @@
  * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
  */
 
-#include "common.hpp"
-
 #include "exclude.hpp"
 
+#include <boost/static_assert.hpp>
+
 namespace ndn {
 
+BOOST_STATIC_ASSERT((boost::is_base_of<tlv::Error, Exclude::Error>::value));
+
 Exclude::Exclude()
 {
 }
@@ -40,6 +42,10 @@
 size_t
 Exclude::wireEncode(EncodingImpl<T>& block) const
 {
+  if (m_exclude.empty()) {
+    throw Error("Exclude filter cannot be empty");
+  }
+
   size_t totalLength = 0;
 
   // Exclude ::= EXCLUDE-TYPE TLV-LENGTH Any? (NameComponent (Any)?)+
@@ -87,9 +93,16 @@
 void
 Exclude::wireDecode(const Block& wire)
 {
+  if (wire.type() != tlv::Exclude)
+    throw tlv::Error("Unexpected TLV type when decoding Exclude");
+
   m_wire = wire;
   m_wire.parse();
 
+  if (m_wire.elements_size() == 0) {
+    throw Error("Exclude element cannot be empty");
+  }
+
   // Exclude ::= EXCLUDE-TYPE TLV-LENGTH Any? (NameComponent (Any)?)+
   // Any     ::= ANY-TYPE TLV-LENGTH(=0)
 
@@ -127,8 +140,6 @@
     }
 }
 
-
-
 // example: ANY /b /d ANY /f
 //
 // ordered in map as:
@@ -166,7 +177,6 @@
   return *this;
 }
 
-
 // example: ANY /b0 /d0 ANY /f0
 //
 // ordered in map as:
@@ -251,7 +261,6 @@
   return *this;
 }
 
-
 std::ostream&
 operator<<(std::ostream& os, const Exclude& exclude)
 {
@@ -271,5 +280,4 @@
   return os;
 }
 
-
 } // namespace ndn
diff --git a/tests/unit-tests/test-exclude.cpp b/tests/unit-tests/test-exclude.cpp
index 1cc708d..38a811a 100644
--- a/tests/unit-tests/test-exclude.cpp
+++ b/tests/unit-tests/test-exclude.cpp
@@ -117,6 +117,49 @@
                       Exclude::Error);
 }
 
+BOOST_AUTO_TEST_CASE(Malformed)
+{
+  Exclude e1;
+  BOOST_CHECK_THROW(e1.wireEncode(), Exclude::Error);
+
+  Exclude e2;
+
+  // top-level TLV-TYPE is not tlv::Exclude
+  const uint8_t NON_EXCLUDE[] = { 0x01, 0x02, 0x13, 0x00 };
+  BOOST_CHECK_THROW(e2.wireDecode(Block(NON_EXCLUDE, sizeof(NON_EXCLUDE))),
+                    tlv::Error);
+
+  // Exclude element is empty
+  const uint8_t EMPTY_EXCLUDE[] = { 0x10, 0x00 };
+  BOOST_CHECK_THROW(e2.wireDecode(Block(EMPTY_EXCLUDE, sizeof(EMPTY_EXCLUDE))),
+                    Exclude::Error);
+
+  // Exclude element contains unknown element
+  const uint8_t UNKNOWN_COMP1[] = { 0x10, 0x02, 0xAA, 0x00 };
+  BOOST_CHECK_THROW(e2.wireDecode(Block(UNKNOWN_COMP1, sizeof(UNKNOWN_COMP1))),
+                    Exclude::Error);
+
+  // Exclude element contains unknown element
+  const uint8_t UNKNOWN_COMP2[] = { 0x10, 0x05, 0x08, 0x01, 0x54, 0xAA, 0x00 };
+  BOOST_CHECK_THROW(e2.wireDecode(Block(UNKNOWN_COMP2, sizeof(UNKNOWN_COMP2))),
+                    Exclude::Error);
+
+  // // <Exclude><Any/></Exclude>
+  // const uint8_t ONLY_ANY[] = { 0x10, 0x02, 0x13, 0x00 };
+  // BOOST_CHECK_THROW(e2.wireDecode(Block(ONLY_ANY, sizeof(ONLY_ANY))),
+  //                   Exclude::Error);
+
+  // <Exclude><Any/><Any/></Exclude>
+  const uint8_t ANY_ANY[] = { 0x10, 0x04, 0x13, 0x00, 0x13, 0x00 };
+  BOOST_CHECK_THROW(e2.wireDecode(Block(ANY_ANY, sizeof(ANY_ANY))),
+                                  Exclude::Error);
+
+  // // <Exclude><Any/><NameComponent>T</NameComponent><Any/></Exclude>
+  // const uint8_t ANY_COMPONENT_ANY[] = { 0x10, 0x07, 0x13, 0x00, 0x08, 0x01, 0x54, 0x13, 0x00 };
+  // BOOST_CHECK_THROW(e2.wireDecode(Block(ANY_COMPONENT_ANY, sizeof(ANY_COMPONENT_ANY))),
+  //                   Exclude::Error);
+}
+
 BOOST_AUTO_TEST_SUITE_END()
 
 } // namespace ndn