Blame SOURCES/0011-src-pathx.c-parse_name-correctly-handle-trailing-whi.patch

91858b
From d157f330acfe94a1f61bf766b485beb0e0dd7177 Mon Sep 17 00:00:00 2001
91858b
From: David Lutterkort <lutter@watzmann.net>
91858b
Date: Fri, 4 Aug 2017 17:13:52 -0700
91858b
Subject: [PATCH] * src/pathx.c (parse_name): correctly handle trailing
91858b
 whitespace in names
91858b
91858b
When a name ended in whitespace, we incorrectly assumed it was always ok to
91858b
trim that whitespace. That is not true if that whitespace is escaped,
91858b
i.e. if the path expression is something like '/x\ '. In that case, the
91858b
name really needs to be literally 'x ', i.e., we can not trim that
91858b
whitespace.
91858b
91858b
The incorrect behavior led to turning '/x\ ' first into 'x\' and then,
91858b
because we assume that '\' is always followed by a character inside the
91858b
string, when we removed the escaping '\', we would read beyond the end of
91858b
the intermediate string result; if we were lucky, that would lead to a
91858b
crash, otherwise we'd continue with junk.
91858b
91858b
We now make sure that escaped whitespace at the end of a string does not
91858b
get stripped, avoiding all these headaches.
91858b
91858b
Fixes RHBZ https://bugzilla.redhat.com/show_bug.cgi?id=1475621
91858b
---
91858b
 src/pathx.c        | 27 ++++++++++++++++-----
91858b
 tests/test-xpath.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++
91858b
 2 files changed, 80 insertions(+), 6 deletions(-)
91858b
91858b
diff --git a/src/pathx.c b/src/pathx.c
91858b
index 8d8dbbbe..a726a032 100644
91858b
--- a/src/pathx.c
91858b
+++ b/src/pathx.c
91858b
@@ -1643,6 +1643,16 @@ int pathx_escape_name(const char *in, char **out) {
91858b
     return 0;
91858b
 }
91858b
 
91858b
+/* Return true if POS is preceded by an odd number of backslashes, i.e., if
91858b
+ * POS is escaped. Stop the search when we get to START */
91858b
+static bool backslash_escaped(const char *pos, const char *start) {
91858b
+    bool result=false;
91858b
+    while (pos-- > start && *pos == '\\') {
91858b
+        result = !result;
91858b
+    }
91858b
+    return result;
91858b
+}
91858b
+
91858b
 /*
91858b
  * NameNoWS ::= [^][|/\= \t\n] | \\.
91858b
  * NameWS   ::= [^][|/\=] | \\.
91858b
@@ -1652,11 +1662,14 @@ static char *parse_name(struct state *state) {
91858b
     const char *s = state->pos;
91858b
     char *result;
91858b
 
91858b
+    /* Advance state->pos until it points to the first character that is
91858b
+     * not part of a name. */
91858b
     while (*state->pos != '\0' && strchr(name_follow, *state->pos) == NULL) {
91858b
-        /* This is a hack: since we allow spaces in names, we need to avoid
91858b
-         * gobbling up stuff that is in follow(Name), e.g. 'or' so that
91858b
-         * things like [name1 or name2] still work.
91858b
-         */
91858b
+        /* Since we allow spaces in names, we need to avoid gobbling up
91858b
+         * stuff that is in follow(Name), e.g. 'or' so that things like
91858b
+         * [name1 or name2] still work. In other words, we'll parse 'x frob
91858b
+         * y' as one name, but for 'x or y', we consider 'x' a name in its
91858b
+         * own right. */
91858b
         if (STREQLEN(state->pos, " or ", strlen(" or ")) ||
91858b
             STREQLEN(state->pos, " and ", strlen(" and ")))
91858b
             break;
91858b
@@ -1671,10 +1684,12 @@ static char *parse_name(struct state *state) {
91858b
         state->pos += 1;
91858b
     }
91858b
 
91858b
-    /* Strip trailing white space */
91858b
+    /* Strip trailing white space. Make sure we respect escaped whitespace
91858b
+     * and don't strip it as in "x\\ " */
91858b
     if (state->pos > s) {
91858b
         state->pos -= 1;
91858b
-        while (isspace(*state->pos) && state->pos >= s)
91858b
+        while (isspace(*state->pos) && state->pos > s
91858b
+               && !backslash_escaped(state->pos, s))
91858b
             state->pos -= 1;
91858b
         state->pos += 1;
91858b
     }
91858b
diff --git a/tests/test-xpath.c b/tests/test-xpath.c
91858b
index 335e7bf8..dbba29e0 100644
91858b
--- a/tests/test-xpath.c
91858b
+++ b/tests/test-xpath.c
91858b
@@ -331,6 +331,62 @@ static int test_wrong_regexp_flag(struct augeas *aug) {
91858b
     return -1;
91858b
 }
91858b
 
91858b
+static int test_trailing_ws_in_name(struct augeas *aug) {
91858b
+    int r;
91858b
+
91858b
+    printf("%-30s ... ", "trailing_ws_in_name");
91858b
+
91858b
+    /* We used to incorrectly lop escaped whitespace off the end of a
91858b
+     * name. Make sure that we really create a tree node with label 'x '
91858b
+     * with the below set, and look for it in a number of ways to ensure we
91858b
+     * are not lopping off trailing whitespace. */
91858b
+    r = aug_set(aug, "/ws\\ ", "1");
91858b
+    if (r < 0) {
91858b
+        fprintf(stderr, "failed to set '/ws ': %d\n", r);
91858b
+        goto fail;
91858b
+    }
91858b
+    /* We did not create a node with label 'ws' */
91858b
+    r = aug_get(aug, "/ws", NULL);
91858b
+    if (r != 0) {
91858b
+        fprintf(stderr, "created '/ws' instead: %d\n", r);
91858b
+        goto fail;
91858b
+    }
91858b
+
91858b
+    /* We did not create a node with label 'ws\t' (this also checks that we
91858b
+     * don't create something like 'ws\\' by dropping the last whitespace
91858b
+     * character. */
91858b
+    r = aug_get(aug, "/ws\\\t", NULL);
91858b
+    if (r != 0) {
91858b
+        fprintf(stderr, "found '/ws\\t': %d\n", r);
91858b
+        goto fail;
91858b
+    }
91858b
+
91858b
+    /* But we did create 'ws ' */
91858b
+    r = aug_get(aug, "/ws\\ ", NULL);
91858b
+    if (r != 1) {
91858b
+        fprintf(stderr, "could not find '/ws ': %d\n", r);
91858b
+        goto fail;
91858b
+    }
91858b
+
91858b
+    /* If the whitespace is preceded by an even number of '\\' chars,
91858b
+     * whitespace must be stripped */
91858b
+    r = aug_set(aug, "/nows\\\\ ", "1");
91858b
+    if (r < 0) {
91858b
+        fprintf(stderr, "set of '/nows' failed: %d\n", r);
91858b
+        goto fail;
91858b
+    }
91858b
+    r = aug_get(aug, "/nows\\\\", NULL);
91858b
+    if (r != 1) {
91858b
+        fprintf(stderr, "could not get '/nows\\'\n");
91858b
+        goto fail;
91858b
+    }
91858b
+    printf("PASS\n");
91858b
+    return 0;
91858b
+ fail:
91858b
+    printf("FAIL\n");
91858b
+    return -1;
91858b
+}
91858b
+
91858b
 static int run_tests(struct test *tests, int argc, char **argv) {
91858b
     char *lensdir;
91858b
     struct augeas *aug = NULL;
91858b
@@ -374,6 +430,9 @@ static int run_tests(struct test *tests, int argc, char **argv) {
91858b
 
91858b
         if (test_wrong_regexp_flag(aug) < 0)
91858b
             result = EXIT_FAILURE;
91858b
+
91858b
+        if (test_trailing_ws_in_name(aug) < 0)
91858b
+            result = EXIT_FAILURE;
91858b
     }
91858b
     aug_close(aug);
91858b
 
91858b
-- 
91858b
2.17.2
91858b