diff options
| -rw-r--r-- | src/shared/json.c | 81 | ||||
| -rw-r--r-- | src/test/test-json.c | 3 | 
2 files changed, 62 insertions, 22 deletions
| diff --git a/src/shared/json.c b/src/shared/json.c index 47f801c858..bb3d26f0e5 100644 --- a/src/shared/json.c +++ b/src/shared/json.c @@ -53,6 +53,42 @@ static void inc_lines(unsigned *line, const char *s, size_t n) {          }  } +static int unhex_ucs2(const char *c, uint16_t *ret) { +        int aa, bb, cc, dd; +        uint16_t x; + +        assert(c); +        assert(ret); + +        aa = unhexchar(c[0]); +        if (aa < 0) +                return -EINVAL; + +        bb = unhexchar(c[1]); +        if (bb < 0) +                return -EINVAL; + +        cc = unhexchar(c[2]); +        if (cc < 0) +                return -EINVAL; + +        dd = unhexchar(c[3]); +        if (dd < 0) +                return -EINVAL; + +        x =     ((uint16_t) aa << 12) | +                ((uint16_t) bb << 8) | +                ((uint16_t) cc << 4) | +                ((uint16_t) dd); + +        if (x <= 0) +                return -EINVAL; + +        *ret = x; + +        return 0; +} +  static int json_parse_string(const char **p, char **ret) {          _cleanup_free_ char *s = NULL;          size_t n = 0, allocated = 0; @@ -119,39 +155,40 @@ static int json_parse_string(const char **p, char **ret) {                          else if (*c == 't')                                  ch = '\t';                          else if (*c == 'u') { -                                int aa, bb, cc, dd;                                  uint16_t x; +                                int r; -                                aa = unhexchar(c[1]); -                                if (aa < 0) -                                        return -EINVAL; +                                r = unhex_ucs2(c + 1, &x); +                                if (r < 0) +                                        return r; -                                bb = unhexchar(c[2]); -                                if (bb < 0) -                                        return -EINVAL; +                                c += 5; -                                cc = unhexchar(c[3]); -                                if (cc < 0) -                                        return -EINVAL; +                                if (!GREEDY_REALLOC(s, allocated, n + 4)) +                                        return -ENOMEM; -                                dd = unhexchar(c[4]); -                                if (dd < 0) +                                if (!utf16_is_surrogate(x)) +                                        n += utf8_encode_unichar(s + n, x); +                                else if (utf16_is_trailing_surrogate(x))                                          return -EINVAL; +                                else { +                                        uint16_t y; +                                        if (c[0] != '\\' || c[1] != 'u') +                                                return -EINVAL; -                                x =     ((uint16_t) aa << 12) | -                                        ((uint16_t) bb << 8) | -                                        ((uint16_t) cc << 4) | -                                        ((uint16_t) dd); +                                        r = unhex_ucs2(c + 2, &y); +                                        if (r < 0) +                                                return r; -                                if (x <= 0) -                                        return -EINVAL; +                                        c += 6; -                                if (!GREEDY_REALLOC(s, allocated, n + 4)) -                                        return -ENOMEM; +                                        if (!utf16_is_trailing_surrogate(y)) +                                                return -EINVAL; + +                                        n += utf8_encode_unichar(s + n, utf16_surrogate_pair_to_unichar(x, y)); +                                } -                                n += utf8_encode_unichar(s + n, x); -                                c += 5;                                  continue;                          } else                                  return -EINVAL; diff --git a/src/test/test-json.c b/src/test/test-json.c index e53e8ed50f..b09131891c 100644 --- a/src/test/test-json.c +++ b/src/test/test-json.c @@ -99,6 +99,9 @@ int main(int argc, char *argv[]) {          test_one("\"\xef\xbf\xbd\"", JSON_STRING, "\xef\xbf\xbd", JSON_END);          test_one("\"\\ufffd\"", JSON_STRING, "\xef\xbf\xbd", JSON_END);          test_one("\"\\uf\"", -EINVAL); +        test_one("\"\\ud800a\"", -EINVAL); +        test_one("\"\\udc00\\udc00\"", -EINVAL); +        test_one("\"\\ud801\\udc37\"", JSON_STRING, "\xf0\x90\x90\xb7", JSON_END);          return 0;  } | 
