[vte] parser: Reject mixed-control OSC and DCS sequences



commit 181752d26586c050204a56616255ac1473abff91
Author: Christian Persch <chpe src gnome org>
Date:   Tue Mar 27 19:40:13 2018 +0200

    parser: Reject mixed-control OSC and DCS sequences
    
    Only accept OSC/DCS sequences where the introducer (OSC or DCS)
    and the string terminator (ST) are either both C1 or C0.
    
    Note that this doesn't regress bug 730154; we still parse the
    sequences but return VTE_SEQ_IGNORE.

 src/parser-test.cc |   50 +++++++++++++++++++++++++++++++++++++-------------
 src/parser.cc      |   36 ++++++++++++++++++++++++++++++++++++
 src/parser.hh      |    1 +
 3 files changed, 74 insertions(+), 13 deletions(-)
---
diff --git a/src/parser-test.cc b/src/parser-test.cc
index 255abea..26d9ceb 100644
--- a/src/parser-test.cc
+++ b/src/parser-test.cc
@@ -1178,13 +1178,14 @@ static int
 feed_parser_st(vte_seq_builder& b,
                bool c1 = false,
                ssize_t max_arg_str_len = -1,
+               u32SequenceBuilder::Introducer introducer = u32SequenceBuilder::Introducer::DEFAULT,
                u32SequenceBuilder::ST st = u32SequenceBuilder::ST::DEFAULT)
 {
         std::u32string s;
-        b.to_string(s, c1, max_arg_str_len, u32SequenceBuilder::Introducer::DEFAULT, st);
+        b.to_string(s, c1, max_arg_str_len, introducer, st);
 
         auto rv = feed_parser(s);
-        if (rv == VTE_SEQ_NONE)
+        if (rv != VTE_SEQ_OSC)
                 return rv;
 
         switch (st) {
@@ -1213,12 +1214,13 @@ test_seq_osc(std::u32string const& str,
              int expected_rv = VTE_SEQ_OSC,
              bool c1 = true,
              ssize_t max_arg_str_len = -1,
+             u32SequenceBuilder::Introducer introducer = u32SequenceBuilder::Introducer::DEFAULT,
              u32SequenceBuilder::ST st = u32SequenceBuilder::ST::DEFAULT)
 {
         vte_seq_builder b{VTE_SEQ_OSC, str};
 
         parser.reset();
-        auto rv = feed_parser_st(b, c1, max_arg_str_len, st);
+        auto rv = feed_parser_st(b, c1, max_arg_str_len, introducer, st);
         g_assert_cmpint(rv, ==, expected_rv);
         #if 0
         if (rv != VTE_SEQ_NONE)
@@ -1234,6 +1236,25 @@ test_seq_osc(std::u32string const& str,
                 g_assert_true(seq.string() == str.substr(0, max_arg_str_len));
 }
 
+static int
+controls_match(bool c1,
+               u32SequenceBuilder::Introducer introducer,
+               u32SequenceBuilder::ST st,
+               bool allow_bel,
+               int expected_rv)
+{
+        if (introducer == u32SequenceBuilder::Introducer::DEFAULT)
+                introducer = c1 ? u32SequenceBuilder::Introducer::C1 : u32SequenceBuilder::Introducer::C0;
+        if (st == u32SequenceBuilder::ST::DEFAULT)
+                st = c1 ? u32SequenceBuilder::ST::C1 : u32SequenceBuilder::ST::C0;
+        if ((introducer == u32SequenceBuilder::Introducer::C0 &&
+             (st == u32SequenceBuilder::ST::C0 || (allow_bel && st == u32SequenceBuilder::ST::BEL))) ||
+            (introducer == u32SequenceBuilder::Introducer::C1 &&
+             st == u32SequenceBuilder::ST::C1))
+                return expected_rv;
+        return VTE_SEQ_IGNORE;
+}
+
 static void
 test_seq_osc(void)
 {
@@ -1249,16 +1270,19 @@ test_seq_osc(void)
         test_seq_osc(std::u32string(VTE_SEQ_STRING_MAX_CAPACITY + 1, 0x100000), VTE_SEQ_IGNORE);
 
         /* Test all introducer/ST combinations */
-        test_seq_osc(U"TEST"s, VTE_SEQ_NONE, false, -1, u32SequenceBuilder::ST::NONE);
-        test_seq_osc(U"TEST"s, VTE_SEQ_NONE, true, -1, u32SequenceBuilder::ST::NONE);
-        test_seq_osc(U"TEST"s, VTE_SEQ_OSC, false, -1, u32SequenceBuilder::ST::DEFAULT);
-        test_seq_osc(U"TEST"s, VTE_SEQ_OSC, true, -1, u32SequenceBuilder::ST::DEFAULT);
-        test_seq_osc(U"TEST"s, VTE_SEQ_OSC, false, -1, u32SequenceBuilder::ST::C0);
-        test_seq_osc(U"TEST"s, VTE_SEQ_OSC, true, -1, u32SequenceBuilder::ST::C0);
-        test_seq_osc(U"TEST"s, VTE_SEQ_OSC, false, -1, u32SequenceBuilder::ST::C1);
-        test_seq_osc(U"TEST"s, VTE_SEQ_OSC, true, -1, u32SequenceBuilder::ST::C1);
-        test_seq_osc(U"TEST"s, VTE_SEQ_OSC, false, -1, u32SequenceBuilder::ST::BEL);
-        test_seq_osc(U"TEST"s, VTE_SEQ_OSC, true, -1, u32SequenceBuilder::ST::BEL);
+        for (auto introducer : { u32SequenceBuilder::Introducer::DEFAULT,
+                                u32SequenceBuilder::Introducer::C0,
+                                u32SequenceBuilder::Introducer::C1 }) {
+                for (auto st : {u32SequenceBuilder::ST::DEFAULT,
+                                        u32SequenceBuilder::ST::C0,
+                                        u32SequenceBuilder::ST::C1,
+                                        u32SequenceBuilder::ST::BEL }) {
+                        for (auto c1 : { false, true }) {
+                                int expected_rv = controls_match(c1, introducer, st, true, VTE_SEQ_OSC);
+                                test_seq_osc(U"TEST"s, expected_rv, c1, -1, introducer, st);
+                        }
+                }
+        }
 }
 
 static void
diff --git a/src/parser.cc b/src/parser.cc
index dd4eb35..7c1bd7a 100644
--- a/src/parser.cc
+++ b/src/parser.cc
@@ -114,6 +114,21 @@
  */
 #define _VTE_SEQ_CODE(f,i) (((f) - 0x40) | ((i) << 6))
 
+/*
+ * @introducer: either a C1 control, or the final in the equivalent ESC F sequence
+ * @terminator: either a C1 control, or the final in the equivalent ESC F sequence
+ *
+ * Checks whether the OSC/DCS @introducer and the ST @terminator
+ * are from the same control set, i.e. both C0 or both C1.
+ *
+ * For OSC, this check allows C0 OSC with BEL-as-ST to pass, too.
+ */
+static inline bool parser_check_matching_controls(uint32_t introducer,
+                                                  uint32_t terminator)
+{
+        return ((introducer ^ terminator) & 0x80) == 0;
+}
+
 static unsigned int vte_parse_host_control(const struct vte_seq *seq)
 {
         switch (seq->terminator) {
@@ -554,6 +569,9 @@ static inline int parser_clear(struct vte_parser *parser, uint32_t raw)
         for (i = 0; i < VTE_PARSER_ARG_MAX; ++i)
                 parser->seq.args[i] = VTE_SEQ_ARG_INIT_DEFAULT;
 
+        /* We don't need to do this, since it's only used when it's been set */
+        /* parser->seq.introducer = 0; */
+
         return VTE_SEQ_NONE;
 }
 
@@ -672,6 +690,7 @@ static inline int parser_osc_start(struct vte_parser *parser, uint32_t raw)
 
         vte_seq_string_reset(&parser->seq.arg_str);
 
+        parser->seq.introducer = raw;
         return VTE_SEQ_NONE;
 }
 
@@ -694,6 +713,7 @@ static int parser_dcs_start(struct vte_parser *parser, uint32_t raw)
 
         vte_seq_string_reset(&parser->seq.arg_str);
 
+        parser->seq.introducer = raw;
         return VTE_SEQ_NONE;
 }
 
@@ -769,6 +789,13 @@ static int parser_osc(struct vte_parser *parser, uint32_t raw)
 
         vte_seq_string_finish(&parser->seq.arg_str);
 
+        /* We only dispatch a DCS if the introducer and string
+         * terminator are from the same control set, i.e. both
+         * C0 or both C1; we discard sequences with mixed controls.
+         */
+        if (!parser_check_matching_controls(parser->seq.introducer, raw))
+                return VTE_SEQ_IGNORE;
+
         parser->seq.type = VTE_SEQ_OSC;
         parser->seq.command = VTE_CMD_OSC;
         parser->seq.terminator = raw;
@@ -781,6 +808,15 @@ static int parser_dcs(struct vte_parser *parser, uint32_t raw)
 {
         /* parser->seq was already filled in parser_dcs_consume() */
 
+        vte_seq_string_finish(&parser->seq.arg_str);
+
+        /* We only dispatch a DCS if the introducer and string
+         * terminator are from the same control set, i.e. both
+         * C0 or both C1; we discard sequences with mixed controls.
+         */
+        if (!parser_check_matching_controls(parser->seq.introducer, raw))
+                return VTE_SEQ_IGNORE;
+
         return parser->seq.type;
 }
 
diff --git a/src/parser.hh b/src/parser.hh
index 94b5af4..2338f4a 100644
--- a/src/parser.hh
+++ b/src/parser.hh
@@ -174,6 +174,7 @@ struct vte_seq {
         unsigned int n_final_args;
         vte_seq_arg_t args[VTE_PARSER_ARG_MAX];
         vte_seq_string_t arg_str;
+        uint32_t introducer;
 };
 
 struct vte_parser {


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]