json_out: pass through OOM failures.
authorRusty Russell <rusty@rustcorp.com.au>
Thu, 23 May 2019 05:29:44 +0000 (14:59 +0930)
committerRusty Russell <rusty@rustcorp.com.au>
Thu, 23 May 2019 05:29:44 +0000 (14:59 +0930)
And fix escaping to work with addstr(), and assert in debug mode
if they don't specify quotes.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ccan/json_out/json_out.c
ccan/json_out/json_out.h

index b8c798fc0dae7527cbf5986da619cdf7cb8ed437..87deaca85f9ce0375ff9bd299ced923669f7c739 100644 (file)
@@ -11,7 +11,7 @@ struct json_out {
        void *cb_arg;
        
 #ifdef CCAN_JSON_OUT_DEBUG
-       /* tal_arr of types ( or [ we're enclosed in. */
+       /* tal_arr of types ( or [ we're enclosed in.  NULL if oom. */
        char *wrapping;
 #endif
        /* True if we haven't yet put an element in current wrapping */
@@ -27,16 +27,23 @@ static void *membuf_tal_realloc(struct membuf *mb,
 {
        char *p = rawelems;
 
-       tal_resize(&p, newsize);
+       if (!tal_resize(&p, newsize))
+               return NULL;
        return p;
 }
 
 struct json_out *json_out_new(const tal_t *ctx)
 {
        struct json_out *jout = tal(ctx, struct json_out);
+       char *pool;
 
-       membuf_init(&jout->outbuf,
-                   tal_arr(jout, char, 64), 64, membuf_tal_realloc);
+       if (!jout)
+               return NULL;
+       pool = tal_arr(jout, char, 64);
+       if (!pool)
+               return tal_free(jout);
+
+       membuf_init(&jout->outbuf, pool, tal_count(pool), membuf_tal_realloc);
 #ifdef CCAN_JSON_OUT_DEBUG
        jout->wrapping = tal_arr(jout, char, 0);
 #endif
@@ -61,9 +68,14 @@ struct json_out *json_out_dup(const tal_t *ctx, const struct json_out *src)
        size_t num_elems = membuf_num_elems(&src->outbuf);
        char *elems = membuf_elems(&src->outbuf);
        struct json_out *jout = tal_dup(ctx, struct json_out, src);
-       membuf_init(&jout->outbuf,
-                   tal_dup_arr(jout, char, elems, num_elems, 0),
-                   num_elems, membuf_tal_realloc);
+       char *pool;
+
+       if (!jout)
+               return NULL;
+       pool = tal_dup_arr(jout, char, elems, num_elems, 0);
+       if (!pool)
+               return tal_free(jout);
+       membuf_init(&jout->outbuf, pool, num_elems, membuf_tal_realloc);
        membuf_added(&jout->outbuf, num_elems);
 #ifdef CCAN_JSON_OUT_DEBUG
        jout->wrapping = tal_dup_arr(jout, char,
@@ -76,9 +88,14 @@ struct json_out *json_out_dup(const tal_t *ctx, const struct json_out *src)
 static void indent(struct json_out *jout, char type)
 {
 #ifdef CCAN_JSON_OUT_DEBUG
-       size_t n = tal_count(jout->wrapping);
-       tal_resize(&jout->wrapping, n+1);
-       jout->wrapping[n] = type;
+       /* Can't check if we ran out of memory. */
+       if (jout->wrapping) {
+               size_t n = tal_count(jout->wrapping);
+               if (!tal_resize(&jout->wrapping, n+1))
+                       jout->wrapping = tal_free(jout->wrapping);
+               else
+                       jout->wrapping[n] = type;
+       }
 #endif
        jout->empty = true;
 }
@@ -86,11 +103,14 @@ static void indent(struct json_out *jout, char type)
 static void unindent(struct json_out *jout, char type)
 {
 #ifdef CCAN_JSON_OUT_DEBUG
-       size_t indent = tal_count(jout->wrapping);
-       assert(indent > 0);
-       /* Both [ and ] and { and } are two apart in ASCII */
-       assert(jout->wrapping[indent-1] == type - 2);
-       tal_resize(&jout->wrapping, indent-1);
+       /* Can't check if we ran out of memory. */
+       if (jout->wrapping) {
+               size_t indent = tal_count(jout->wrapping);
+               assert(indent > 0);
+               /* Both [ and ] and { and } are two apart in ASCII */
+               assert(jout->wrapping[indent-1] == type - 2);
+               tal_resize(&jout->wrapping, indent-1);
+       }
 #endif
        jout->empty = false;
 }
@@ -110,18 +130,22 @@ static void check_fieldname(const struct json_out *jout,
                            const char *fieldname)
 {
 #ifdef CCAN_JSON_OUT_DEBUG
-       size_t n = tal_count(jout->wrapping);
-       if (n == 0)
-               /* Can't have a fieldname if not in anything! */
-               assert(!fieldname);
-       else if (jout->wrapping[n-1] == '[')
-               /* No fieldnames in arrays. */
-               assert(!fieldname);
-       else {
-               /* Must have fieldnames in objects. */
-               assert(fieldname);
-               /* We don't escape this for you */
-               assert(!json_escape_needed(fieldname, strlen(fieldname)));
+       /* We don't escape this for you */
+       assert(!fieldname || !json_escape_needed(fieldname, strlen(fieldname)));
+
+       /* Can't check anything else if we ran out of memory. */
+       if (jout->wrapping) {
+               size_t n = tal_count(jout->wrapping);
+               if (n == 0)
+                       /* Can't have a fieldname if not in anything! */
+                       assert(!fieldname);
+               else if (jout->wrapping[n-1] == '[')
+                       /* No fieldnames in arrays. */
+                       assert(!fieldname);
+               else {
+                       /* Must have fieldnames in objects. */
+                       assert(fieldname);
+               }
        }
 #endif
 }
@@ -139,12 +163,9 @@ char *json_out_member_direct(struct json_out *jout,
        if (fieldname)
                extra += 1 + strlen(fieldname) + 2;
 
-       if (!extra) {
-               dest = NULL;
-               goto out;
-       }
-
        dest = mkroom(jout, extra);
+       if (!dest)
+               goto out;
 
        if (!jout->empty)
                *(dest++) = ',';
@@ -162,21 +183,33 @@ out:
        return dest;
 }
 
-void json_out_start(struct json_out *jout, const char *fieldname, char type)
+bool json_out_start(struct json_out *jout, const char *fieldname, char type)
 {
+       char *p;
+
        assert(type == '[' || type == '{');
-       json_out_member_direct(jout, fieldname, 1)[0] = type;
+       p = json_out_member_direct(jout, fieldname, 1);
+       if (p)
+               p[0] = type;
        indent(jout, type);
+
+       return p != NULL;
 }
 
-void json_out_end(struct json_out *jout, char type)
+bool json_out_end(struct json_out *jout, char type)
 {
+       char *p;
+
        assert(type == '}' || type == ']');
-       json_out_direct(jout, 1)[0] = type;
+       p = json_out_direct(jout, 1);
+       if (p)
+               p[0] = type;
        unindent(jout, type);
+
+       return p != NULL;
 }
 
-void json_out_addv(struct json_out *jout,
+bool json_out_addv(struct json_out *jout,
                   const char *fieldname,
                   bool quote,
                   const char *fmt,
@@ -186,7 +219,8 @@ void json_out_addv(struct json_out *jout,
        va_list ap2;
        char *dst;
 
-       json_out_member_direct(jout, fieldname, 0);
+       if (!json_out_member_direct(jout, fieldname, 0))
+               return false;
 
        /* Make a copy in case we need it below. */
        va_copy(ap2, ap);
@@ -210,52 +244,78 @@ void json_out_addv(struct json_out *jout,
        if (fmtlen + (int)quote*2 >= membuf_num_space(&jout->outbuf)) {
                /* Make room for NUL terminator, even though we don't want it */
                dst = mkroom(jout, fmtlen + 1 + (int)quote*2);
+               if (!dst)
+                       goto out;
                vsprintf(dst + quote, fmt, ap2);
        }
 
-       /* Of course, if we need to escape it, we have to redo it all. */
-       if (json_escape_needed(dst + quote, fmtlen)) {
-               struct json_escape *e;
-               e = json_escape_len(NULL, dst + quote, fmtlen);
-               fmtlen = strlen(e->s);
-               dst = mkroom(jout, fmtlen + (int)quote*2);
-               memcpy(dst + quote, e, fmtlen);
-               tal_free(e);
-       }
+#ifdef CCAN_JSON_OUT_DEBUG
+       /* You're not inserting junk here, are you? */
+       assert(quote || !json_escape_needed(dst, fmtlen));
+#endif
 
+       /* Of course, if we need to escape it, we have to redo it all. */
        if (quote) {
+               if (json_escape_needed(dst + quote, fmtlen)) {
+                       struct json_escape *e;
+                       e = json_escape_len(NULL, dst + quote, fmtlen);
+                       fmtlen = strlen(e->s);
+                       dst = mkroom(jout, fmtlen + (int)quote*2);
+                       if (!dst)
+                               goto out;
+                       memcpy(dst + quote, e, fmtlen);
+                       tal_free(e);
+               }
                dst[0] = '"';
                dst[fmtlen+1] = '"';
        }
        membuf_added(&jout->outbuf, fmtlen + (int)quote*2);
+
+out:
        va_end(ap2);
+       return dst != NULL;
 }
 
-void json_out_add(struct json_out *jout,
+bool json_out_add(struct json_out *jout,
                  const char *fieldname,
                  bool quote,
                  const char *fmt, ...)
 {
        va_list ap;
+       bool ret;
 
        va_start(ap, fmt);
-       json_out_addv(jout, fieldname, quote, fmt, ap);
+       ret = json_out_addv(jout, fieldname, quote, fmt, ap);
        va_end(ap);
+       return ret;
 }
 
-void json_out_addstr(struct json_out *jout,
+bool json_out_addstr(struct json_out *jout,
                     const char *fieldname,
                     const char *str)
 {
        size_t len = strlen(str);
        char *p;
+       struct json_escape *e;
+
+       if (json_escape_needed(str, len)) {
+               e = json_escape(NULL, str);
+               str = e->s;
+               len = strlen(str);
+       } else
+               e = NULL;
 
        p = json_out_member_direct(jout, fieldname, len + 2);
-       p[0] = p[1+len] = '"';
-       memcpy(p+1, str, len);
+       if (p) {
+               p[0] = p[1+len] = '"';
+               memcpy(p+1, str, len);
+       }
+       tal_free(e);
+
+       return p != NULL;
 }
 
-void json_out_add_splice(struct json_out *jout,
+bool json_out_add_splice(struct json_out *jout,
                         const char *fieldname,
                         const struct json_out *src)
 {
@@ -263,13 +323,17 @@ void json_out_add_splice(struct json_out *jout,
        size_t len;
 
        p = json_out_contents(src, &len);
+       if (!p)
+               return false;
        memcpy(json_out_member_direct(jout, fieldname, len), p, len);
+       return true;
 }
 
 char *json_out_direct(struct json_out *jout, size_t len)
 {
        char *p = mkroom(jout, len);
-       membuf_added(&jout->outbuf, len);
+       if (p)
+               membuf_added(&jout->outbuf, len);
        return p;
 }
 
index a3ebdb13e384af7bc27751608fcae96bc443f7eb..2911ff2473bac5cd029eff02558c4fe6ef48bb37 100644 (file)
@@ -11,6 +11,8 @@ struct json_out;
 /**
  * json_out_new - allocate a json_out stream.
  * @ctx: the tal_context to allocate from, or NULL
+ *
+ * Returns NULL if tal allocation fails.
  */
 struct json_out *json_out_new(const tal_t *ctx);
 
@@ -50,20 +52,23 @@ struct json_out *json_out_dup(const tal_t *ctx, const struct json_out *src);
  * @fieldname: the fieldname, if inside an object, or NULL if inside an array.
  * @type: '[' or '{' to start an array or object, respectively.
  *
+ * Returns true unless tal_resize() fails.
  * Literally writes '"@fieldname": @type' or '@type ' if fieldname is NULL.
  * @fieldname must not need JSON escaping.
  */
-void json_out_start(struct json_out *jout, const char *fieldname, char type);
+bool json_out_start(struct json_out *jout, const char *fieldname, char type);
 
 /**
  * json_out_end - end an array or object.
  * @jout: the json_out object to write into.
  * @type: '}' or ']' to end an array or object, respectively.
  *
+ * Returns true unless tal_resize() fails.
+ *
  * Literally writes ']' or '}', keeping track of whether we need to append
  * a comma.
  */
-void json_out_end(struct json_out *jout, char type);
+bool json_out_end(struct json_out *jout, char type);
 
 /**
  * json_out_add - add a formatted member.
@@ -72,15 +77,18 @@ void json_out_end(struct json_out *jout, char type);
  * @quote: if true, surround fmt by " and ".
  * @fmt...: the printf-style format
  *
+ * Returns true unless tal_resize() fails.
+ *
  * If you're in an array, @fieldname must be NULL.  If you're in an
  * object, @fieldname must be non-NULL.  This is checked if
  * CCAN_JSON_OUT_DEBUG is defined.
  * @fieldname must not need JSON escaping.
  *
- * If the resulting string requires escaping, we call json_escape().
+ * If the resulting string requires escaping, and @quote is true, we
+ * call json_escape().
  */
 PRINTF_FMT(4,5)
-void json_out_add(struct json_out *jout,
+bool json_out_add(struct json_out *jout,
                  const char *fieldname,
                  bool quote,
                  const char *fmt, ...);
@@ -95,7 +103,7 @@ void json_out_add(struct json_out *jout,
  *
  * See json_out_add() above.
  */
-void json_out_addv(struct json_out *jout,
+bool json_out_addv(struct json_out *jout,
                   const char *fieldname,
                   bool quote,
                   const char *fmt,
@@ -109,7 +117,7 @@ void json_out_addv(struct json_out *jout,
  *
  * Equivalent to json_out_add(@jout, @fieldname, true, "%s", @str);
  */
-void json_out_addstr(struct json_out *jout,
+bool json_out_addstr(struct json_out *jout,
                     const char *fieldname,
                     const char *str);
 
@@ -120,7 +128,7 @@ void json_out_addstr(struct json_out *jout,
  * @extra: how many bytes to allocate.
  *
  * @fieldname must not need JSON escaping.  Returns a direct pointer into
- * the @extra bytes.
+ * the @extra bytes, or NULL if tal_resize() fails.
  *
  * This allows you to write your own efficient type-specific helpers.
  */
@@ -135,7 +143,8 @@ char *json_out_member_direct(struct json_out *jout,
  * This lets you access the json_out stream directly, to save a copy,
  * if you know exactly how much you will write.
  *
- * Returns a pointer to @len bytes at the end of @jout.
+ * Returns a pointer to @len bytes at the end of @jout, or NULL if
+ * tal_resize() fails.
  *
  * This is dangerous, since it doesn't automatically prepend a ","
  * like the internal logic does, but can be used (carefully) to add
@@ -156,8 +165,10 @@ char *json_out_direct(struct json_out *jout, size_t extra);
  *
  * Note that it will call json_out_contents(@src), so it expects that
  * object to be unconsumed.
+ *
+ * Returns false if tal_resize() fails.
  */
-void json_out_add_splice(struct json_out *jout,
+bool json_out_add_splice(struct json_out *jout,
                         const char *fieldname,
                         const struct json_out *src);