Re: svn commit: r1914814 - in /apr/apr/trunk: CHANGES encoding/apr_escape.c include/apr_escape.h test/testescape.c tools/gen_test_char.c

Ruediger Pluem <[email protected]>
Newsgroups gmane.comp.apache.apr.devel
Message-ID <[email protected]>

On 12/20/23 11:46 PM, [email protected] wrote:
> Author: minfrin
> Date: Wed Dec 20 22:46:22 2023
> New Revision: 1914814
> 
> URL: http://svn.apache.org/viewvc?rev=1914814&view=rev
> Log:
> apr_escape: Add apr_escape_json() and apr_pescape_json().
> 
> Modified:
>     apr/apr/trunk/CHANGES
>     apr/apr/trunk/encoding/apr_escape.c
>     apr/apr/trunk/include/apr_escape.h
>     apr/apr/trunk/test/testescape.c
>     apr/apr/trunk/tools/gen_test_char.c
> 

> Modified: apr/apr/trunk/encoding/apr_escape.c
> URL: http://svn.apache.org/viewvc/apr/apr/trunk/encoding/apr_escape.c?rev=1914814&r1=1914813&r2=1914814&view=diff
> ==============================================================================
> --- apr/apr/trunk/encoding/apr_escape.c (original)
> +++ apr/apr/trunk/encoding/apr_escape.c Wed Dec 20 22:46:22 2023
> @@ -36,7 +36,7 @@
>   * char in here and get it to work, because if char is signed then it
>   * will first be sign extended.
>   */
> -#define TEST_CHAR(c, f)        (test_char_table[(unsigned)(c)] & (f))
> +#define TEST_CHAR(c, f)        (test_char_table[(apr_uint16_t)(c)] & (f))
>  
>  APR_DECLARE(apr_status_t) apr_escape_shell(char *escaped, const char *str,
>          apr_ssize_t slen, apr_size_t *len)
> @@ -1209,6 +1209,271 @@ APR_DECLARE(const char *) apr_pescape_ld
>      }
>      }
>  
> +    return src;
> +}
> +
> +APR_DECLARE(apr_status_t) apr_escape_json(char *escaped, const char *str,
> +        apr_ssize_t slen, int quote, apr_size_t *len)

Shouldn't we provide a length for the escaped buffer to be able to do some sanity checking against overflows?
We could ignore this parameter in case that escaped is NULL.
Otherwise I think we should have a big fat warning in the doxygen documentation of this function that it
is extremely dangerous to call this function without providing a buffer of sufficient length and that
apr_escape_json should be called with escaped equal NULL before to determine this length, just like as
we do in apr_pescape_json.

> +{
> +    apr_size_t size = 1;
> +    int found = quote;
> +    int error = 0;
> +    const unsigned char *s = (const unsigned char *) str;
> +    unsigned char *d = (unsigned char *) escaped;
> +    unsigned c;
> +    const char invalid[3] = { 0xEF, 0xBF, 0xBD };
> +
> +    if (s) {
> +        if (d) {
> +            if (quote) {
> +                *d++ = '"';
> +                size += 1;
> +            }
> +            while ((c = *s) && slen) {
> +                if (TEST_CHAR(c, T_ESCAPE_JSON)) {
> +                    switch (c) {
> +                    case '\b':
> +                        *d++ = '\\';
> +                        *d++ = 'b';
> +                        size += 2;
> +                        found = 1;
> +                        break;
> +                    case '\f':
> +                        *d++ = '\\';
> +                        *d++ = 'f';
> +                        size += 2;
> +                        found = 1;
> +                        break;
> +                    case '\n':
> +                        *d++ = '\\';
> +                        *d++ = 'n';
> +                        size += 2;
> +                        found = 1;
> +                        break;
> +                    case '\r':
> +                        *d++ = '\\';
> +                        *d++ = 'r';
> +                        size += 2;
> +                        found = 1;
> +                        break;
> +                    case '\t':
> +                        *d++ = '\\';
> +                        *d++ = 't';
> +                        size += 2;
> +                        found = 1;
> +                        break;
> +                    case '\\':
> +                        *d++ = '\\';
> +                        *d++ = '\\';
> +                        size += 2;
> +                        found = 1;
> +                        break;
> +                    case '"':
> +                        *d++ = '\\';
> +                        *d++ = '"';
> +                        size += 2; 
> +                        found = 1;
> +                        break;
> +                    default:
> +                        if (c < 0x20) {
> +                            size += apr_snprintf((char *)d, 6, "\\u%04x", c);
> +                            d += 5; 
> +                            found = 1;
> +                        }
> +                        else if (((c >> 7) == 0x00)) {

How can this case happen? c's like this should fail the TEST_CHAR(c, T_ESCAPE_JSON) test.

> +                            /* 1 byte */
> +                            memcpy(d, s, 1);
> +                            d += 1;
> +                            size += 1;
> +                        }
> +                        else if ((slen < 0 || slen > 1) && ((c >> 5) == 0x06) && s[1]) {
> +                            /* 2 bytes */
> +                            if ((slen > 0 && slen < 2) || (s[1] >> 6) != 0x02) {
> +                                memcpy(d, invalid, sizeof(invalid));
> +                                d += sizeof(invalid);
> +                                size += sizeof(invalid);
> +                                found = error = 1;
> +                            }
> +                            else {
> +                                memcpy(d, s, 2);
> +                                d += 2;
> +                                size += 2;
> +                                s += 1;
> +                                slen -= 1;
> +                            }
> +                        }
> +                        else if (((c >> 4) == 0x0E)) {
> +                            /* 3 bytes */
> +                            if ((slen > 0 && slen < 3) || (s[1] >> 6) != 0x02 || (s[2] >> 6) != 0x02) {
> +                                memcpy(d, invalid, sizeof(invalid));
> +                                size += sizeof(invalid);
> +                                d += sizeof(invalid);
> +                                found = error = 1;
> +                            }
> +                            else {
> +                                memcpy(d, s, 3);
> +                                d += 3;
> +                                size += 3;
> +                                s += 2;
> +                                slen -= 2;
> +                            }
> +                        }
> +                        else if ((c >> 3) == 0x1E) {
> +                            /* 4 bytes */
> +                            if ((slen > 0 && slen < 4) || (s[1] >> 6) != 0x02 || (s[2] >> 6) != 0x02 || (s[3] >> 6) != 0x02) {
> +                                memcpy(d, invalid, sizeof(invalid));
> +                                d += sizeof(invalid);
> +                                size += sizeof(invalid);
> +                                found = error = 1;
> +                            }
> +                            else {
> +                                memcpy(d, s, 4);
> +                                d += 4;
> +                                size += 4;
> +                                s += 3;
> +                                slen -= 3;
> +                            }
> +                        }
> +                        else {
> +                            memcpy(d, invalid, sizeof(invalid));
> +                            d += sizeof(invalid);
> +                            size += sizeof(invalid);
> +                            found = error = 1;
> +                        }
> +                        break;
> +                    }
> +                }
> +                else {
> +                    *d++ = c;
> +                    size++;
> +                }
> +                ++s;
> +                slen--;
> +            }
> +            if (quote) {
> +                *d++ = '"';
> +                size += 1;
> +            }
> +            *d = '\0';
> +        }
> +        else {
> +            if (quote) {
> +                size += 1;
> +            }
> +            while ((c = *s) && slen) {
> +                if (TEST_CHAR(c, T_ESCAPE_JSON)) {
> +                    switch (c) {
> +                    case '\b':
> +                    case '\f':
> +                    case '\n':
> +                    case '\r':
> +                    case '\t':
> +                    case '\\':
> +                    case '"':
> +                        size += 2;
> +                        found = 1;
> +                        break;
> +                    default:
> +                        if (c < 0x20) {
> +                            size += 5;
> +                            found = 1;
> +                        }
> +                        else if (((c >> 7) == 0x00)) {

How can this case happen? c's like this should fail the TEST_CHAR(c, T_ESCAPE_JSON) test.

> +                            /* 1 byte */
> +                            size += 1;
> +                        }
> +                        else if ((slen < 0 || slen > 1) && ((c >> 5) == 0x06) && s[1]) {
> +                            /* 2 bytes */
> +                            if ((slen > 0 && slen < 2) || (s[1] >> 6) != 0x02) {
> +                                size += sizeof(invalid);
> +                                found = error = 1;
> +                            }
> +                            else {
> +                                size += 2;
> +                                s += 1;
> +                                slen -= 1;
> +                            }
> +                        }
> +                        else if (((c >> 4) == 0x0E)) {
> +                            /* 3 bytes */
> +                            if ((slen > 0 && slen < 3) || (s[1] >> 6) != 0x02 || (s[2] >> 6) != 0x02) {
> +                                size += sizeof(invalid);
> +                                found = error = 1;
> +                            }
> +                            else {
> +                                size += 3;
> +                                s += 2;
> +                                slen -= 2;
> +                            }
> +                        }
> +                        else if ((c >> 3) == 0x1E) {
> +                            /* 4 bytes */
> +                            if ((slen > 0 && slen < 4) || (s[1] >> 6) != 0x02 || (s[2] >> 6) != 0x02 || (s[3] >> 6) != 0x02) {
> +                                size += sizeof(invalid);
> +                                found = error = 1;
> +                            }
> +                            else {
> +                                size += 4;
> +                                s += 3;
> +                                slen -= 3;
> +                            }
> +                        }
> +                        else {
> +                            size += sizeof(invalid);
> +                            found = error = 1;
> +                        }
> +                    }
> +                }
> +                else {
> +                    size++;
> +                }
> +                ++s;
> +                slen--;
> +            }
> +            if (quote) {
> +                size += 1;
> +            }
> +        }
> +    }
> +
> +    else if (quote) {
> +        if (d) {
> +            memcpy(d, "NULL", 5);
> +        }
> +        else {
> +            size += 4;
> +        }
> +    }
> +
> +    if (len) {
> +        *len = size;
> +    }
> +    if (error) {
> +        return APR_EINVAL;
> +    }
> +    if (!found) {
> +        return APR_NOTFOUND;
> +    }
> +
> +    return APR_SUCCESS;
> +}
> +
> +APR_DECLARE(const char *) apr_pescape_json(apr_pool_t *p, const char *src,
> +        apr_ssize_t srclen, int quote)

The caller cannot easily determine if the src contained invalid data or was already
properly escaped.
Can't we do a

APR_DECLARE(apr_status_t) pr_pescape_json(apr_pool_t *p, char **dest, const char *src,
        apr_ssize_t srclen, int quote)

instead which returns the status of apr_escape_json and the escaped JSON in dest?


> +{
> +    apr_size_t len;
> +
> +    switch (apr_escape_json(NULL, src, srclen, quote, &len)) {
> +    case APR_NOTFOUND: {
> +        break;
> +    }
> +    default: {
> +        char *encoded = apr_palloc(p, len);
> +        apr_escape_json(encoded, src, srclen, quote, NULL);
> +        return encoded;
> +    }
> +    }
> +
>      return src;
>  }
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.