CVE-2020-23914: NULL Pointer Dereference (CWE-476)
value: An issue was discovered in cpp-peglib through v0.1.12. A NULL pointer dereference exists in the peg::AstOptimizer::optimize() located in peglib.h. It allows an attacker to cause Denial of Service.
CWE description: The product dereferences a pointer that it expects to be valid but is NULL.
CVSS v3 base score: 5.5
Published: 2021-04-21T18:15Z
Repository: https://github.com/yhirose/cpp-peglib
Language: C++
Files changed: peglib.h, test/test1.cc
Commit date: 2020-08-07 10:21:47 -0400
Commit message: Fix #121

Diff: diff --git a/peglib.h b/peglib.h
index b285339..fc949de 100644
--- a/peglib.h
+++ b/peglib.h
@@ -1237,16 +1237,14 @@ public:
 
 class LiteralString : public Ope,
                       public std::enable_shared_from_this<LiteralString> {
 public:
   LiteralString(std::string &&s, bool ignore_case)
-      : lit_(s), ignore_case_(ignore_case),
-        is_word_(false) {}
+      : lit_(s), ignore_case_(ignore_case), is_word_(false) {}
 
   LiteralString(const std::string &s, bool ignore_case)
-      : lit_(s), ignore_case_(ignore_case),
-        is_word_(false) {}
+      : lit_(s), ignore_case_(ignore_case), is_word_(false) {}
 
   size_t parse_core(const char *s, size_t n, SemanticValues &sv, Context &c,
                     any &dt) const override;
 
   void accept(Visitor &v) override;
@@ -3436,10 +3434,25 @@ private:
         auto line = line_info(s, ptr);
         log(line.first, line.second, "'" + name + "' is already defined.");
       }
     }
 
+    // Check if the start rule has ignore operator
+    {
+      auto &rule = grammar[data.start];
+      if (rule.ignoreSemanticValue) {
+        if (log) {
+          auto line = line_info(s, rule.s_);
+          log(line.first, line.second,
+              "Ignore operator cannot be applied to '" + rule.name + "'.");
+        }
+        ret = false;
+      }
+    }
+
+    if (!ret) { return nullptr; }
+
     // Check missing definitions
     for (auto &x : grammar) {
       auto &rule = x.second;
 
       ReferenceChecker vis(*data.grammar, rule.params);
diff --git a/test/test1.cc b/test/test1.cc
index 4f8f586..32e6706 100644
--- a/test/test1.cc
+++ b/test/test1.cc
@@ -32,10 +32,21 @@ TEST_CASE("Empty syntax test", "[general]")
     parser parser("");
     bool ret = parser;
     REQUIRE(ret == false);
 }
 
+TEST_CASE("Start rule with ignore operator test", "[general]")
+{
+    parser parser(R"(
+        ~ROOT <- _
+        _ <- ' '
+    )");
+
+    bool ret = parser;
+    REQUIRE(ret == false);
+}
+
 TEST_CASE("Backslash escape sequence test", "[general]")
 {
     parser parser(R"(
         ROOT <- _
         _ <- '\\'

Vulnerable code:       : lit_(s), ignore_case_(ignore_case),
        is_word_(false) {}
      : lit_(s), ignore_case_(ignore_case),
        is_word_(false) {}
Fixed code:       : lit_(s), ignore_case_(ignore_case), is_word_(false) {}
      : lit_(s), ignore_case_(ignore_case), is_word_(false) {}
    // Check if the start rule has ignore operator
    {
      auto &rule = grammar[data.start];
      if (rule.ignoreSemanticValue) {
        if (log) {
          auto line = line_info(s, rule.s_);
          log(line.first, line.second,
              "Ignore operator cannot be applied to '" + rule.name + "'.");
        }
        ret = false;
      }
    }

    if (!ret) { return nullptr; }

TEST_CASE("Start rule with ignore operator test", "[general]")
{
    parser parser(R"(
        ~ROOT <- _
        _ <- ' '
    )");

    bool ret = parser;
    REQUIRE(ret == false);
}