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

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