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

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