Am 19.10.18 um 17:56 schrieb David Sommerseth:
> From: Heiko Hund <heiko.h...@sophos.com>
> 
> The previous implementation had the problem that it was not fully
> compatible with printf() and could only detect % format directives
> following a space character (0x20).
> 
> It modifies the format string and inserts marks to separate groups
> before passing it to the regular printf in libc. The marks are
> later used to separate the output string into individual command
> line arguments.
> 
> The choice of 0x1D as the argument delimiter is based on the
> assumption that no "regular" string passed to argv_printf_*() will
> ever have to contain that byte (and the fact that it actually is
> the ASCII "group separator" control character, which fits its
> purpose).
> 
> This commit has been updated by David Sommerseth based on his feedback
> on the mailing list discussions earlier on.
> 
> Signed-off-by: Heiko Hund <heiko.h...@sophos.com>
> Signed-off-by: David Sommerseth <dav...@openvpn.net>
> ---
>  src/openvpn/argv.c                   | 266 ++++++++++++---------------
>  src/openvpn/argv.h                   |   4 +-
>  src/openvpn/route.c                  |   8 +-
>  src/openvpn/tun.c                    |  24 +--
>  tests/unit_tests/openvpn/test_argv.c |  58 +++++-
>  5 files changed, 192 insertions(+), 168 deletions(-)
> 
> diff --git a/src/openvpn/argv.c b/src/openvpn/argv.c
> index 9100a196..9606f382 100644
> --- a/src/openvpn/argv.c
> +++ b/src/openvpn/argv.c
> @@ -131,64 +131,6 @@ argv_insert_head(const struct argv *a, const char *head)
>      return r;
>  }
>  
> -static char *
> -argv_term(const char **f)
> -{
> -    const char *p = *f;
> -    const char *term = NULL;
> -    size_t termlen = 0;
> -
> -    if (*p == '\0')
> -    {
> -        return NULL;
> -    }
> -
> -    while (true)
> -    {
> -        const int c = *p;
> -        if (c == '\0')
> -        {
> -            break;
> -        }
> -        if (term)
> -        {
> -            if (!isspace(c))
> -            {
> -                ++termlen;
> -            }
> -            else
> -            {
> -                break;
> -            }
> -        }
> -        else
> -        {
> -            if (!isspace(c))
> -            {
> -                term = p;
> -                termlen = 1;
> -            }
> -        }
> -        ++p;
> -    }
> -    *f = p;
> -
> -    if (term)
> -    {
> -        char *ret;
> -        ASSERT(termlen > 0);
> -        ret = malloc(termlen + 1);
> -        check_malloc_return(ret);
> -        memcpy(ret, term, termlen);
> -        ret[termlen] = '\0';
> -        return ret;
> -    }
> -    else
> -    {
> -        return NULL;
> -    }
> -}
> -
>  const char *
>  argv_str(const struct argv *a, struct gc_arena *gc, const unsigned int flags)
>  {
> @@ -218,119 +160,151 @@ argv_msg_prefix(const int msglev, const struct argv 
> *a, const char *prefix)
>      gc_free(&gc);
>  }
>  
> -static void
> +
> +/*
> + * argv_prep_format - prepare argv format string for further processing
> + *
> + * Individual argument must be separated by space. Ignores leading and 
> trailing spaces.
> + * Consecutive spaces count as one. Returns prepared format string, with 
> space replaced
> + * by delim and adds the number of arguments to the count parameter.
> + */
> +static char *
> +argv_prep_format(const char *format, const char delim, size_t *count, struct 
> gc_arena *gc)
> +{

Lines longer than 80 chars (90).

> +    int i, j;
> +    char *f;
All could be made inline with C99

> +    bool in_token = false;
> +
> +    if (format == NULL)
> +    {
> +        return NULL;
> +    }
> +
> +    f = gc_malloc(strlen(format) + 1, true, gc);
> +    for (i = 0, j = 0; i < strlen(format); i++)
> +    {
int i=0, j=0;

> +        if (format[i] == ' ')
> +        {
> +            in_token = false;
> +            continue;
> +        }
> +
> +        if (!in_token)
> +        {
> +            ++*count;

I am entirely sure about our style here but I would prefer brackets
around (*count). And since you use i++ and j++, also should use (*count)++;

> +
> +            /*
> +             * We don't add any delimiter to the resulting
> +             * format string before something has been added there.

This sentence is hard to understand. "before something has been added
there" => if it is empty?

> +             * The resulting format string will never start with a delimiter.

Okay. But why? I think this is done to ignore, leading spaces?

> +             */
> +            if (j > 0)
> +            {
> +                f[j++] = delim;
> +            }

[...]

> +/*
> + * argv_printf_arglist - create a struct argv from a format string
> + *
> + * Instead of parsing the format string ourselves place delimiters via 
> argv_prep_format()
> + * before we let libc's printf() do the parsing. Then split the resulting 
> string at the
> + * injected delimiters.
> + */
> +static bool
>  argv_printf_arglist(struct argv *a, const char *format, va_list arglist)
>  {
> -    char *term;
> -    const char *f = format;
> +    struct gc_arena gc = gc_new();
> +    const char delim = 0x1D;  /* ASCII Group Separator (GS) */
> +    char *f, *buf, *end;
> +    size_t argc, size;
> +    bool res = false;
> +    va_list tmplist;
> +    int len;

Making some of these inline or move their defintion closer where they
are actually would make the code easier to understand.


>      argv_extend(a, 1); /* ensure trailing NULL */

This depends on the side effect of the allocating nulling things before.
Not sure if that is okay or not.

> -    while ((term = argv_term(&f)) != NULL)
> +    argc = a->argc;
> +    f = argv_prep_format(format, delim, &argc, &gc);
> +    if (f == NULL)
>      {
> -        if (term[0] == '%')
> -        {
> -            if (!strcmp(term, "%s"))
> -            {
> -                char *s = va_arg(arglist, char *);
> -                if (!s)
> -                {
> -                    s = "";
> -                }
> -                argv_append(a, string_alloc(s, NULL));
> -            }
> -            else if (!strcmp(term, "%d"))
> -            {
> -                char numstr[64];
> -                openvpn_snprintf(numstr, sizeof(numstr), "%d", 
> va_arg(arglist, int));
> -                argv_append(a, string_alloc(numstr, NULL));
> -            }
> -            else if (!strcmp(term, "%u"))
> -            {
> -                char numstr[64];
> -                openvpn_snprintf(numstr, sizeof(numstr), "%u", 
> va_arg(arglist, unsigned int));
> -                argv_append(a, string_alloc(numstr, NULL));
> -            }
> -            else if (!strcmp(term, "%lu"))
> -            {
> -                char numstr[64];
> -                openvpn_snprintf(numstr, sizeof(numstr), "%lu",
> -                                 va_arg(arglist, unsigned long));
> -                argv_append(a, string_alloc(numstr, NULL));
> -            }
> -            else if (!strcmp(term, "%s/%d"))
> -            {
> -                char numstr[64];
> -                char *s = va_arg(arglist, char *);
> -
> -                if (!s)
> -                {
> -                    s = "";
> -                }
> +        goto out;
> +    }
>  
> -                openvpn_snprintf(numstr, sizeof(numstr), "%d", 
> va_arg(arglist, int));
> +    /* determine minimum buffer size */
> +    va_copy(tmplist, arglist);
> +    len = vsnprintf(NULL, 0, f, tmplist);
> +    va_end(tmplist);
> +    if (len < 0)
> +    {
> +        goto out;
> +    }
>  
> -                {
> -                    const size_t len = strlen(s) + strlen(numstr) + 2;
> -                    char *combined = (char *) malloc(len);
> -                    check_malloc_return(combined);
> +    size = adjust_power_of_2(len + 1);

I am a bit sure why we need to increase the size to the next power of 2
and why len+1 shouldn't be enough

> +    buf = gc_malloc(size, false, &gc);
> +    len = vsnprintf(buf, size, f, arglist);
> +    if (len < 0 || len >= size)
> +    {
> +        goto out;
> +    }


This vnsprintf should need the same space as the previous vsnprintf that
was used to determine len.

>  
> -                    strcpy(combined, s);
> -                    strcat(combined, "/");
> -                    strcat(combined, numstr);
> -                    argv_append(a, combined);
> -                }
> -            }
> -            else if (!strcmp(term, "%s%sc"))
> -            {
> -                char *s1 = va_arg(arglist, char *);
> -                char *s2 = va_arg(arglist, char *);
> -                char *combined;
> +    /* split the string at the delimiters */
> +    end = strchr(buf, delim);
> +    while (end)
> +    {
> +        *end = '\0';
> +        argv_append(a, string_alloc(buf, NULL));
> +        buf = end + 1;
> +        end = strchr(buf, delim);
> +    }
> +    argv_append(a, string_alloc(buf, NULL));
>  
> -                if (!s1)
> -                {
> -                    s1 = "";
> -                }
> -                if (!s2)
> -                {
> -                    s2 = "";
> -                }
> -                combined = (char *) malloc(strlen(s1) + strlen(s2) + 1);
> -                check_malloc_return(combined);
> -                strcpy(combined, s1);
> -                strcat(combined, s2);
> -                argv_append(a, combined);
> -            }
> -            else
> -            {
> -                ASSERT(0);
> -            }
> -            free(term);
> -        }
> -        else
> -        {
> -            argv_append(a, term);
> -        }
> +    if (a->argc != argc)
> +    {
> +        /* Someone snuck in a GS (0x1D), fail gracefully */
> +        argv_reset(a);
> +        argv_extend(a, 1); /* ensure trailing NULL */
> +        goto out;
>      }

That is a bit weird way to check that but okay.


> -void
> +
> +
> +bool
>  argv_printf(struct argv *a, const char *format, ...)
>  {
> +    bool res;
>      va_list arglist;
> -    argv_reset(a);
> +
>      va_start(arglist, format);
> -    argv_printf_arglist(a, format, arglist);
> +    argv_reset(a);
> +    res = argv_printf_arglist(a, format, arglist);
>      va_end(arglist);
> +    return res;
>  }

The bool definition could be made inline (bool res=).




> -void
> +bool
>  argv_printf_cat(struct argv *a, const char *format, ...)
>  {
> +    bool res;
>      va_list arglist;
>      va_start(arglist, format);
> -    argv_printf_arglist(a, format, arglist);
> +    res = argv_printf_arglist(a, format, arglist);
>      va_end(arglist);
> +    return res;
>  }

Same here.


Rest looks good.

Arne

Attachment: signature.asc
Description: OpenPGP digital signature

_______________________________________________
Openvpn-devel mailing list
Openvpn-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/openvpn-devel

Reply via email to