Check SFLV tuple is within stream before decoding

If the SFLV tuple being decoded goes out of the stream length
it could lead to a heap overflow. This patch adds a check to
prevent it.

Tested:
Unit tested

Signed-off-by: Kasun Athukorala <kasunath@google.com>
Change-Id: Ie018d4f3614603efc6ed8b93fa0077c612d9b481
diff --git a/test/bej_decoder_test.cpp b/test/bej_decoder_test.cpp
index 7869dfb..cf76d2b 100644
--- a/test/bej_decoder_test.cpp
+++ b/test/bej_decoder_test.cpp
@@ -82,6 +82,25 @@
     EXPECT_TRUE(jsonDecoded.dump() == inputsOrErr->expectedJson.dump());
 }
 
+/**
+ * TODO: Add more test cases.
+ * - Test Enums inside array elemets
+ * - Array inside an array: is this a valid case?
+ * - Real numbers with exponent part
+ * - Every type inside an array.
+ */
+INSTANTIATE_TEST_SUITE_P(
+    , BejDecoderTest,
+    testing::ValuesIn<BejDecoderTestParams>({
+        {"DriveOEM", driveOemTestFiles},
+        {"Circuit", circuitTestFiles},
+        {"Storage", storageTestFiles},
+        {"DummySimple", dummySimpleTestFiles},
+    }),
+    [](const testing::TestParamInfo<BejDecoderTest::ParamType>& info) {
+        return info.param.testName;
+    });
+
 TEST(BejDecoderSecurityTest, MaxOperationsLimit)
 {
     auto inputsOrErr = loadInputs(dummySimpleTestFiles);
@@ -232,23 +251,35 @@
                 bejErrorInvalidSize);
 }
 
-/**
- * TODO: Add more test cases.
- * - Test Enums inside array elemets
- * - Array inside an array: is this a valid case?
- * - Real numbers with exponent part
- * - Every type inside an array.
- */
-INSTANTIATE_TEST_SUITE_P(
-    , BejDecoderTest,
-    testing::ValuesIn<BejDecoderTestParams>({
-        {"DriveOEM", driveOemTestFiles},
-        {"Circuit", circuitTestFiles},
-        {"Storage", storageTestFiles},
-        {"DummySimple", dummySimpleTestFiles},
-    }),
-    [](const testing::TestParamInfo<BejDecoderTest::ParamType>& info) {
-        return info.param.testName;
-    });
+TEST(BejDecoderSecurityTest, ValueBeyondStreamLength)
+{
+    auto inputsOrErr = loadInputs(dummySimpleTestFiles);
+    ASSERT_TRUE(inputsOrErr);
+
+    BejDictionaries dictionaries = {
+        .schemaDictionary = inputsOrErr->schemaDictionary,
+        .annotationDictionary = inputsOrErr->annotationDictionary,
+        .errorDictionary = inputsOrErr->errorDictionary,
+    };
+
+    auto root = std::make_unique<RedfishPropertyParent>();
+    bejTreeInitSet(root.get(), "DummySimple");
+
+    auto intProp = std::make_unique<RedfishPropertyLeafInt>();
+    bejTreeAddInteger(root.get(), intProp.get(), "SampleIntegerProperty", 123);
+
+    libbej::BejEncoderJson encoder;
+    encoder.encode(&dictionaries, bejMajorSchemaClass, root.get());
+    std::vector<uint8_t> outputBuffer = encoder.getOutput();
+
+    // Tamper with the encoded stream to simulate a value extending beyond the
+    // stream length. This stream only has an integer 0x7b. The value before it
+    // is the length tuple.
+    outputBuffer[outputBuffer.size() - 2] = 0x05;
+
+    BejDecoderJson decoder;
+    EXPECT_THAT(decoder.decode(dictionaries, std::span(outputBuffer)),
+                bejErrorInvalidSize);
+}
 
 } // namespace libbej