diff --git a/configure.in b/configure.in
index 04af24163be43d7b567f91943af9a00acb529f37..d2868a28b56c6afc1f2cde4714a65e00f479305a 100644
--- a/configure.in
+++ b/configure.in
@@ -200,7 +200,7 @@ dnl -------------------------------------------------------------------
 dnl Check for functions before libevent, since libevent-1.2 apparently
 dnl exports strlcpy without defining it in a header.
 
-AC_CHECK_FUNCS(gettimeofday ftime socketpair uname inet_aton strptime getrlimit strlcat strlcpy strtoull getaddrinfo localtime_r gmtime_r memmem strtok_r writev readv flock prctl)
+AC_CHECK_FUNCS(gettimeofday ftime socketpair uname inet_aton strptime getrlimit strlcat strlcpy strtoull getaddrinfo localtime_r gmtime_r memmem strtok_r writev readv flock prctl vasprintf)
 
 using_custom_malloc=no
 if test x$enable_openbsd_malloc = xyes ; then
diff --git a/src/common/compat.c b/src/common/compat.c
index 7f53704c6905e089a4fbbf4a6a7e5277d4c33f15..26038c1099a407d9d640a0960c8d2e6b5b633a0d 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -307,6 +307,100 @@ tor_vsnprintf(char *str, size_t size, const char *format, va_list args)
   return r;
 }
 
+/**
+ * Portable asprintf implementation.  Does a printf() into a newly malloc'd
+ * string.  Sets *<b>strp</b> to this string, and returns its length (not
+ * including the terminating NUL character).
+ *
+ * You can treat this function as if its implementation were something like
+   <pre>
+     char buf[_INFINITY_];
+     tor_snprintf(buf, sizeof(buf), fmt, args);
+     *strp = tor_strdup(buf);
+     return strlen(*strp):
+   </pre>
+ * Where _INFINITY_ is an imaginary constant so big that any string can fit
+ * into it.
+ */
+int
+tor_asprintf(char **strp, const char *fmt, ...)
+{
+  int r;
+  va_list args;
+  va_start(args, fmt);
+  r = tor_vasprintf(strp, fmt, args);
+  va_end(args);
+  if (!*strp || r < 0) {
+    log_err(LD_BUG, "Internal error in asprintf");
+    tor_assert(0);
+  }
+  return r;
+}
+
+/**
+ * Portable vasprintf implementation.  Does a printf() into a newly malloc'd
+ * string.  Differs from regular vasprintf in the same ways that
+ * tor_asprintf() differs from regular asprintf.
+ */
+int
+tor_vasprintf(char **strp, const char *fmt, va_list args)
+{
+  /* use a temporary variable in case *strp is in args. */
+  char *strp_tmp=NULL;
+#ifdef HAVE_VASPRINTF
+  /* If the platform gives us one, use it. */
+  int r = vasprintf(&strp_tmp, fmt, args);
+  if (r < 0)
+    *strp = NULL;
+  else
+    *strp = strp_tmp;
+  return r;
+#elif defined(MS_WINDOWS)
+  /* On Windows, _vsnprintf won't tell us the length of the string if it
+   * overflows, so we need to use _vcsprintf to tell how much to allocate */
+  int len, r;
+  char *res;
+  len = _vcsprintf(fmt, args);
+  if (len < 0) {
+    *strp = NULL;
+    return -1;
+  }
+  strp_tmp = tor_malloc(len + 1);
+  r = _vsnprintf(strp_tmp, len+1, fmt, args);
+  if (r != len) {
+    tor_free(strp_tmp);
+    *strp = NULL;
+    return -1;
+  }
+  *strp = strp_tmp;
+  return len;
+#else
+  /* Everywhere else, we have a decent vsnprintf that tells us how many
+   * characters we need.  We give it a try on a short buffer first, since
+   * it might be nice to avoid the second vsnprintf call.
+   */
+  char buf[128];
+  int len, r;
+  va_list tmp_args;
+  va_copy(tmp_args, args);
+  len = vsnprintf(buf, sizeof(buf), fmt, tmp_args);
+  va_end(tmp_args);
+  if (len < (int)sizeof(buf)) {
+    *strp = tor_strdup(buf);
+    return len;
+  }
+  strp_tmp = tor_malloc(len+1);
+  r = vsnprintf(strp_tmp, len+1, fmt, args);
+  if (r != len) {
+    tor_free(strp_tmp);
+    *strp = NULL;
+    return -1;
+  }
+  *strp = strp_tmp;
+  return len;
+#endif
+}
+
 /** Given <b>hlen</b> bytes at <b>haystack</b> and <b>nlen</b> bytes at
  * <b>needle</b>, return a pointer to the first occurrence of the needle
  * within the haystack, or NULL if there is no such occurrence.
diff --git a/src/common/compat.h b/src/common/compat.h
index f5f8bb4283d5d0bc904964180715b8e7061d8cb5..dbadd605092100b44cf26aa10a7eb4f8f671fe40 100644
--- a/src/common/compat.h
+++ b/src/common/compat.h
@@ -243,6 +243,10 @@ int tor_snprintf(char *str, size_t size, const char *format, ...)
 int tor_vsnprintf(char *str, size_t size, const char *format, va_list args)
   ATTR_NONNULL((1,3));
 
+int tor_asprintf(char **strp, const char *fmt, ...)
+  CHECK_PRINTF(2,3);
+int tor_vasprintf(char **strp, const char *fmt, va_list args);
+
 const void *tor_memmem(const void *haystack, size_t hlen, const void *needle,
                        size_t nlen)  ATTR_PURE ATTR_NONNULL((1,3));
 static const void *tor_memstr(const void *haystack, size_t hlen,
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index cd5ada8dce4bd7af0f20a9abf3dd3cb86d7a8388..233d60f15c1f5456331885592b586c86abf5fd7d 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -1085,21 +1085,21 @@ circuit_list_path_impl(origin_circuit_t *circ, int verbose, int verbose_names)
   crypt_path_t *hop;
   smartlist_t *elements;
   const char *states[] = {"closed", "waiting for keys", "open"};
-  char buf[128];
   char *s;
 
   elements = smartlist_create();
 
   if (verbose) {
     const char *nickname = build_state_get_exit_nickname(circ->build_state);
-    tor_snprintf(buf, sizeof(buf), "%s%s circ (length %d%s%s):",
+    char *cp;
+    tor_asprintf(&cp, "%s%s circ (length %d%s%s):",
                  circ->build_state->is_internal ? "internal" : "exit",
                  circ->build_state->need_uptime ? " (high-uptime)" : "",
                  circ->build_state->desired_path_len,
                  circ->_base.state == CIRCUIT_STATE_OPEN ? "" : ", exit ",
                  circ->_base.state == CIRCUIT_STATE_OPEN ? "" :
                  (nickname?nickname:"*unnamed*"));
-    smartlist_add(elements, tor_strdup(buf));
+    smartlist_add(elements, cp);
   }
 
   hop = circ->cpath;
@@ -3068,21 +3068,21 @@ static void
 log_entry_guards(int severity)
 {
   smartlist_t *elements = smartlist_create();
-  char buf[1024];
   char *s;
 
   SMARTLIST_FOREACH(entry_guards, entry_guard_t *, e,
     {
       const char *msg = NULL;
+      char *cp;
       if (entry_is_live(e, 0, 1, 0, &msg))
-        tor_snprintf(buf, sizeof(buf), "%s (up %s)",
+        tor_asprintf(&cp, "%s (up %s)",
                      e->nickname,
                      e->made_contact ? "made-contact" : "never-contacted");
       else
-        tor_snprintf(buf, sizeof(buf), "%s (%s, %s)",
+        tor_asprintf(&cp, "%s (%s, %s)",
                      e->nickname, msg,
                      e->made_contact ? "made-contact" : "never-contacted");
-      smartlist_add(elements, tor_strdup(buf));
+      smartlist_add(elements, cp);
     });
 
   s = smartlist_join_strings(elements, ",", 0, NULL);
diff --git a/src/or/config.c b/src/or/config.c
index dee2303bfa22c5860aceb08d497a82753164e2a3..5d07cd7343aaf63e0b54700648fcc334bcfd528d 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -957,11 +957,9 @@ options_act_reversible(or_options_t *old_options, char **msg)
   /* Ensure data directory is private; create if possible. */
   if (check_private_dir(options->DataDirectory,
                         running_tor ? CPD_CREATE : CPD_CHECK)<0) {
-    char buf[1024];
-    int tmp = tor_snprintf(buf, sizeof(buf),
+    tor_asprintf(msg,
               "Couldn't access/create private data directory \"%s\"",
               options->DataDirectory);
-    *msg = tor_strdup(tmp >= 0 ? buf : "internal error");
     goto done;
     /* No need to roll back, since you can't change the value. */
   }
@@ -972,10 +970,8 @@ options_act_reversible(or_options_t *old_options, char **msg)
     tor_snprintf(fn, len, "%s"PATH_SEPARATOR"cached-status",
                  options->DataDirectory);
     if (check_private_dir(fn, running_tor ? CPD_CREATE : CPD_CHECK) < 0) {
-      char buf[1024];
-      int tmp = tor_snprintf(buf, sizeof(buf),
+      tor_asprintf(msg,
                 "Couldn't access/create private data directory \"%s\"", fn);
-      *msg = tor_strdup(tmp >= 0 ? buf : "internal error");
       tor_free(fn);
       goto done;
     }
@@ -1546,8 +1542,7 @@ static int
 config_assign_value(config_format_t *fmt, or_options_t *options,
                     config_line_t *c, char **msg)
 {
-  int i, r, ok;
-  char buf[1024];
+  int i, ok;
   config_var_t *var;
   void *lvalue;
 
@@ -1563,10 +1558,9 @@ config_assign_value(config_format_t *fmt, or_options_t *options,
   case CONFIG_TYPE_UINT:
     i = (int)tor_parse_long(c->value, 10, 0, INT_MAX, &ok, NULL);
     if (!ok) {
-      r = tor_snprintf(buf, sizeof(buf),
+      tor_asprintf(msg,
           "Int keyword '%s %s' is malformed or out of bounds.",
           c->key, c->value);
-      *msg = tor_strdup(r >= 0 ? buf : "internal error");
       return -1;
     }
     *(int *)lvalue = i;
@@ -1575,10 +1569,9 @@ config_assign_value(config_format_t *fmt, or_options_t *options,
   case CONFIG_TYPE_INTERVAL: {
     i = config_parse_interval(c->value, &ok);
     if (!ok) {
-      r = tor_snprintf(buf, sizeof(buf),
+      tor_asprintf(msg,
           "Interval '%s %s' is malformed or out of bounds.",
           c->key, c->value);
-      *msg = tor_strdup(r >= 0 ? buf : "internal error");
       return -1;
     }
     *(int *)lvalue = i;
@@ -1588,10 +1581,9 @@ config_assign_value(config_format_t *fmt, or_options_t *options,
   case CONFIG_TYPE_MEMUNIT: {
     uint64_t u64 = config_parse_memunit(c->value, &ok);
     if (!ok) {
-      r = tor_snprintf(buf, sizeof(buf),
+      tor_asprintf(msg,
           "Value '%s %s' is malformed or out of bounds.",
           c->key, c->value);
-      *msg = tor_strdup(r >= 0 ? buf : "internal error");
       return -1;
     }
     *(uint64_t *)lvalue = u64;
@@ -1601,10 +1593,9 @@ config_assign_value(config_format_t *fmt, or_options_t *options,
   case CONFIG_TYPE_BOOL:
     i = (int)tor_parse_long(c->value, 10, 0, 1, &ok, NULL);
     if (!ok) {
-      r = tor_snprintf(buf, sizeof(buf),
+      tor_asprintf(msg,
           "Boolean '%s %s' expects 0 or 1.",
           c->key, c->value);
-      *msg = tor_strdup(r >= 0 ? buf : "internal error");
       return -1;
     }
     *(int *)lvalue = i;
@@ -1622,9 +1613,8 @@ config_assign_value(config_format_t *fmt, or_options_t *options,
 
   case CONFIG_TYPE_ISOTIME:
     if (parse_iso_time(c->value, (time_t *)lvalue)) {
-      r = tor_snprintf(buf, sizeof(buf),
+      tor_asprintf(msg,
           "Invalid time '%s' for keyword '%s'", c->value, c->key);
-      *msg = tor_strdup(r >= 0 ? buf : "internal error");
       return -1;
     }
     break;
@@ -1635,9 +1625,8 @@ config_assign_value(config_format_t *fmt, or_options_t *options,
     }
     *(routerset_t**)lvalue = routerset_new();
     if (routerset_parse(*(routerset_t**)lvalue, c->value, c->key)<0) {
-      tor_snprintf(buf, sizeof(buf), "Invalid exit list '%s' for option '%s'",
+      tor_asprintf(msg, "Invalid exit list '%s' for option '%s'",
                    c->value, c->key);
-      *msg = tor_strdup(buf);
       return -1;
     }
     break;
@@ -1662,9 +1651,8 @@ config_assign_value(config_format_t *fmt, or_options_t *options,
     log_warn(LD_CONFIG, "Skipping obsolete configuration option '%s'", c->key);
     break;
   case CONFIG_TYPE_LINELIST_V:
-    r = tor_snprintf(buf, sizeof(buf),
+    tor_asprintf(msg,
         "You may not provide a value for virtual option '%s'", c->key);
-    *msg = tor_strdup(r >= 0 ? buf : "internal error");
     return -1;
   default:
     tor_assert(0);
@@ -1700,10 +1688,8 @@ config_assign_line(config_format_t *fmt, or_options_t *options,
       config_line_append((config_line_t**)lvalue, c->key, c->value);
       return 0;
     } else {
-      char buf[1024];
-      int tmp = tor_snprintf(buf, sizeof(buf),
+      tor_asprintf(msg,
                 "Unknown option '%s'.  Failing.", c->key);
-      *msg = tor_strdup(tmp >= 0 ? buf : "internal error");
       return -1;
     }
   }
@@ -1829,7 +1815,6 @@ get_assigned_option(config_format_t *fmt, void *options,
 {
   config_var_t *var;
   const void *value;
-  char buf[32];
   config_line_t *result;
   tor_assert(options && key);
 
@@ -1870,19 +1855,16 @@ get_assigned_option(config_format_t *fmt, void *options,
     case CONFIG_TYPE_UINT:
       /* This means every or_options_t uint or bool element
        * needs to be an int. Not, say, a uint16_t or char. */
-      tor_snprintf(buf, sizeof(buf), "%d", *(int*)value);
-      result->value = tor_strdup(buf);
+      tor_asprintf(&result->value, "%d", *(int*)value);
       escape_val = 0; /* Can't need escape. */
       break;
     case CONFIG_TYPE_MEMUNIT:
-      tor_snprintf(buf, sizeof(buf), U64_FORMAT,
+      tor_asprintf(&result->value, U64_FORMAT,
                    U64_PRINTF_ARG(*(uint64_t*)value));
-      result->value = tor_strdup(buf);
       escape_val = 0; /* Can't need escape. */
       break;
     case CONFIG_TYPE_DOUBLE:
-      tor_snprintf(buf, sizeof(buf), "%f", *(double*)value);
-      result->value = tor_strdup(buf);
+      tor_asprintf(&result->value, "%f", *(double*)value);
       escape_val = 0; /* Can't need escape. */
       break;
     case CONFIG_TYPE_BOOL:
@@ -2605,15 +2587,10 @@ config_dump(config_format_t *fmt, void *options, int minimal,
     line = assigned = get_assigned_option(fmt, options, fmt->vars[i].name, 1);
 
     for (; line; line = line->next) {
-      size_t len = strlen(line->key) + strlen(line->value) + 5;
       char *tmp;
-      tmp = tor_malloc(len);
-      if (tor_snprintf(tmp, len, "%s%s %s\n",
-                       comment_option ? "# " : "",
-                       line->key, line->value)<0) {
-        log_err(LD_BUG,"Internal error writing option value");
-        tor_assert(0);
-      }
+      tor_asprintf(&tmp, "%s%s %s\n",
+                   comment_option ? "# " : "",
+                   line->key, line->value);
       smartlist_add(elements, tmp);
     }
     config_free_lines(assigned);
@@ -2622,13 +2599,8 @@ config_dump(config_format_t *fmt, void *options, int minimal,
   if (fmt->extra) {
     line = *(config_line_t**)STRUCT_VAR_P(options, fmt->extra->var_offset);
     for (; line; line = line->next) {
-      size_t len = strlen(line->key) + strlen(line->value) + 3;
       char *tmp;
-      tmp = tor_malloc(len);
-      if (tor_snprintf(tmp, len, "%s %s\n", line->key, line->value)<0) {
-        log_err(LD_BUG,"Internal error writing option value");
-        tor_assert(0);
-      }
+      tor_asprintf(&tmp, "%s %s\n", line->key, line->value);
       smartlist_add(elements, tmp);
     }
   }
@@ -2657,7 +2629,6 @@ static int
 validate_ports_csv(smartlist_t *sl, const char *name, char **msg)
 {
   int i;
-  char buf[1024];
   tor_assert(name);
 
   if (!sl)
@@ -2667,9 +2638,7 @@ validate_ports_csv(smartlist_t *sl, const char *name, char **msg)
   {
     i = atoi(cp);
     if (i < 1 || i > 65535) {
-      int r = tor_snprintf(buf, sizeof(buf),
-                           "Port '%s' out of range in %s", cp, name);
-      *msg = tor_strdup(r >= 0 ? buf : "internal error");
+      tor_asprintf(msg, "Port '%s' out of range in %s", cp, name);
       return -1;
     }
   });
@@ -2683,18 +2652,15 @@ validate_ports_csv(smartlist_t *sl, const char *name, char **msg)
 static int
 ensure_bandwidth_cap(uint64_t *value, const char *desc, char **msg)
 {
-  int r;
-  char buf[1024];
   if (*value > ROUTER_MAX_DECLARED_BANDWIDTH) {
     /* This handles an understandable special case where somebody says "2gb"
      * whereas our actual maximum is 2gb-1 (INT_MAX) */
     --*value;
   }
   if (*value > ROUTER_MAX_DECLARED_BANDWIDTH) {
-    r = tor_snprintf(buf, sizeof(buf), "%s ("U64_FORMAT") must be at most %d",
-                     desc, U64_PRINTF_ARG(*value),
-                     ROUTER_MAX_DECLARED_BANDWIDTH);
-    *msg = tor_strdup(r >= 0 ? buf : "internal error");
+    tor_asprintf(msg, "%s ("U64_FORMAT") must be at most %d",
+                 desc, U64_PRINTF_ARG(*value),
+                 ROUTER_MAX_DECLARED_BANDWIDTH);
     return -1;
   }
   return 0;
@@ -2769,10 +2735,9 @@ static int
 options_validate(or_options_t *old_options, or_options_t *options,
                  int from_setconf, char **msg)
 {
-  int i, r;
+  int i;
   config_line_t *cl;
   const char *uname = get_uname();
-  char buf[1024];
 #define REJECT(arg) \
   STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END
 #define COMPLAIN(arg) STMT_BEGIN log(LOG_WARN, LD_CONFIG, arg); STMT_END
@@ -2867,10 +2832,9 @@ options_validate(or_options_t *old_options, or_options_t *options,
     }
   } else {
     if (!is_legal_nickname(options->Nickname)) {
-      r = tor_snprintf(buf, sizeof(buf),
+      tor_asprintf(msg,
           "Nickname '%s' is wrong length or contains illegal characters.",
           options->Nickname);
-      *msg = tor_strdup(r >= 0 ? buf : "internal error");
       return -1;
     }
   }
@@ -3019,10 +2983,9 @@ options_validate(or_options_t *old_options, or_options_t *options,
            "FetchDirInfoEarly");
 
   if (options->ConnLimit <= 0) {
-    r = tor_snprintf(buf, sizeof(buf),
+    tor_asprintf(msg,
         "ConnLimit must be greater than 0, but was set to %d",
         options->ConnLimit);
-    *msg = tor_strdup(r >= 0 ? buf : "internal error");
     return -1;
   }
 
@@ -3141,9 +3104,8 @@ options_validate(or_options_t *old_options, or_options_t *options,
         else if (!strcasecmp(cp, "rendezvous"))
           options->_AllowInvalid |= ALLOW_INVALID_RENDEZVOUS;
         else {
-          r = tor_snprintf(buf, sizeof(buf),
+          tor_asprintf(msg,
               "Unrecognized value '%s' in AllowInvalidNodes", cp);
-          *msg = tor_strdup(r >= 0 ? buf : "internal error");
           return -1;
         }
       });
@@ -3157,17 +3119,14 @@ options_validate(or_options_t *old_options, or_options_t *options,
   } else if (!strcasecmp(options->SafeLogging, "1")) {
     options->_SafeLogging = SAFELOG_SCRUB_ALL;
   } else {
-    r = tor_snprintf(buf, sizeof(buf),
+    tor_asprintf(msg,
                      "Unrecognized value '%s' in SafeLogging",
                      escaped(options->SafeLogging));
-    *msg = tor_strdup(r >= 0 ? buf : "internal error");
     return -1;
   }
 
   if (compute_publishserverdescriptor(options) < 0) {
-    r = tor_snprintf(buf, sizeof(buf),
-                     "Unrecognized value in PublishServerDescriptor");
-    *msg = tor_strdup(r >= 0 ? buf : "internal error");
+    tor_asprintf(msg, "Unrecognized value in PublishServerDescriptor");
     return -1;
   }
 
@@ -3238,31 +3197,28 @@ options_validate(or_options_t *old_options, or_options_t *options,
 
   if (server_mode(options)) {
     if (options->BandwidthRate < ROUTER_REQUIRED_MIN_BANDWIDTH) {
-      r = tor_snprintf(buf, sizeof(buf),
+      tor_asprintf(msg,
                        "BandwidthRate is set to %d bytes/second. "
                        "For servers, it must be at least %d.",
                        (int)options->BandwidthRate,
                        ROUTER_REQUIRED_MIN_BANDWIDTH);
-      *msg = tor_strdup(r >= 0 ? buf : "internal error");
       return -1;
     } else if (options->MaxAdvertisedBandwidth <
                ROUTER_REQUIRED_MIN_BANDWIDTH/2) {
-      r = tor_snprintf(buf, sizeof(buf),
+      tor_asprintf(msg,
                        "MaxAdvertisedBandwidth is set to %d bytes/second. "
                        "For servers, it must be at least %d.",
                        (int)options->MaxAdvertisedBandwidth,
                        ROUTER_REQUIRED_MIN_BANDWIDTH/2);
-      *msg = tor_strdup(r >= 0 ? buf : "internal error");
       return -1;
     }
     if (options->RelayBandwidthRate &&
       options->RelayBandwidthRate < ROUTER_REQUIRED_MIN_BANDWIDTH) {
-      r = tor_snprintf(buf, sizeof(buf),
+      tor_asprintf(msg,
                        "RelayBandwidthRate is set to %d bytes/second. "
                        "For servers, it must be at least %d.",
                        (int)options->RelayBandwidthRate,
                        ROUTER_REQUIRED_MIN_BANDWIDTH);
-      *msg = tor_strdup(r >= 0 ? buf : "internal error");
       return -1;
     }
   }
@@ -3450,11 +3406,10 @@ options_validate(or_options_t *old_options, or_options_t *options,
     if (options->ConstrainedSockSize < MIN_CONSTRAINED_TCP_BUFFER ||
         options->ConstrainedSockSize > MAX_CONSTRAINED_TCP_BUFFER ||
         options->ConstrainedSockSize % 1024) {
-      r = tor_snprintf(buf, sizeof(buf),
+      tor_asprintf(msg,
           "ConstrainedSockSize is invalid.  Must be a value between %d and %d "
           "in 1024 byte increments.",
           MIN_CONSTRAINED_TCP_BUFFER, MAX_CONSTRAINED_TCP_BUFFER);
-      *msg = tor_strdup(r >= 0 ? buf : "internal error");
       return -1;
     }
     if (options->DirPort) {
@@ -3630,12 +3585,10 @@ options_transition_allowed(or_options_t *old, or_options_t *new_val,
   }
 
   if (strcmp(old->DataDirectory,new_val->DataDirectory)!=0) {
-    char buf[1024];
-    int r = tor_snprintf(buf, sizeof(buf),
+    tor_asprintf(msg,
                "While Tor is running, changing DataDirectory "
                "(\"%s\"->\"%s\") is not allowed.",
                old->DataDirectory, new_val->DataDirectory);
-    *msg = tor_strdup(r >= 0 ? buf : "internal error");
     return -1;
   }
 
@@ -3819,10 +3772,7 @@ check_nickname_list(const char *lst, const char *name, char **msg)
   SMARTLIST_FOREACH(sl, const char *, s,
     {
       if (!is_legal_nickname_or_hexdigest(s)) {
-        char buf[1024];
-        int tmp = tor_snprintf(buf, sizeof(buf),
-                  "Invalid nickname '%s' in %s line", s, name);
-        *msg = tor_strdup(tmp >= 0 ? buf : "internal error");
+        tor_asprintf(msg, "Invalid nickname '%s' in %s line", s, name);
         r = -1;
         break;
       }
@@ -4125,12 +4075,9 @@ options_init_from_string(const char *cf,
  err:
   config_free(&options_format, newoptions);
   if (*msg) {
-    int len = (int)strlen(*msg)+256;
-    char *newmsg = tor_malloc(len);
-
-    tor_snprintf(newmsg, len, "Failed to parse/validate config: %s", *msg);
-    tor_free(*msg);
-    *msg = newmsg;
+    char *old_msg = *msg;
+    tor_asprintf(msg, "Failed to parse/validate config: %s", old_msg);
+    tor_free(old_msg);
   }
   return err;
 }
@@ -4550,7 +4497,6 @@ write_configuration_file(const char *fname, or_options_t *options)
 {
   char *old_val=NULL, *new_val=NULL, *new_conf=NULL;
   int rename_old = 0, r;
-  size_t len;
 
   tor_assert(fname);
 
@@ -4577,9 +4523,7 @@ write_configuration_file(const char *fname, or_options_t *options)
     goto err;
   }
 
-  len = strlen(new_conf)+256;
-  new_val = tor_malloc(len);
-  tor_snprintf(new_val, len, "%s\n%s\n\n%s",
+  tor_asprintf(&new_val, "%s\n%s\n\n%s",
                GENERATED_FILE_PREFIX, GENERATED_FILE_COMMENT, new_conf);
 
   if (rename_old) {
@@ -5023,7 +4967,6 @@ or_state_save(time_t now)
 {
   char *state, *contents;
   char tbuf[ISO_TIME_LEN+1];
-  size_t len;
   char *fname;
 
   tor_assert(global_state);
@@ -5041,15 +4984,11 @@ or_state_save(time_t now)
 
   global_state->LastWritten = time(NULL);
   tor_free(global_state->TorVersion);
-  len = strlen(get_version())+8;
-  global_state->TorVersion = tor_malloc(len);
-  tor_snprintf(global_state->TorVersion, len, "Tor %s", get_version());
+  tor_asprintf(&global_state->TorVersion, "Tor %s", get_version());
 
   state = config_dump(&state_format, global_state, 1, 0);
-  len = strlen(state)+256;
-  contents = tor_malloc(len);
   format_local_iso_time(tbuf, time(NULL));
-  tor_snprintf(contents, len,
+  tor_asprintf(&contents,
                "# Tor state file last generated on %s local time\n"
                "# Other times below are in GMT\n"
                "# You *do not* need to edit this file.\n\n%s",
@@ -5103,7 +5042,6 @@ getinfo_helper_config(control_connection_t *conn,
       config_var_t *var = &_option_vars[i];
       const char *type;
       char *line;
-      size_t len;
       switch (var->type) {
         case CONFIG_TYPE_STRING: type = "String"; break;
         case CONFIG_TYPE_FILENAME: type = "Filename"; break;
@@ -5124,9 +5062,7 @@ getinfo_helper_config(control_connection_t *conn,
       }
       if (!type)
         continue;
-      len = strlen(var->name)+strlen(type)+16;
-      line = tor_malloc(len);
-      tor_snprintf(line, len, "%s %s\n",var->name,type);
+      tor_asprintf(&line, "%s %s\n",var->name,type);
       smartlist_add(sl, line);
     }
     *answer = smartlist_join_strings(sl, "", 0, NULL);
diff --git a/src/or/control.c b/src/or/control.c
index 771beaeb58f2da0412ec4670ba88170c5fb1086e..5797edfdcef599580c5d5b828d7fb1e4b1c946c2 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -1883,18 +1883,18 @@ static char *
 list_getinfo_options(void)
 {
   int i;
-  char buf[300];
+  char *buf=NULL;
   smartlist_t *lines = smartlist_create();
   char *ans;
   for (i = 0; getinfo_items[i].varname; ++i) {
     if (!getinfo_items[i].desc)
       continue;
 
-    tor_snprintf(buf, sizeof(buf), "%s%s -- %s\n",
+    tor_asprintf(&buf, "%s%s -- %s\n",
                  getinfo_items[i].varname,
                  getinfo_items[i].is_prefix ? "*" : "",
                  getinfo_items[i].desc);
-    smartlist_add(lines, tor_strdup(buf));
+    smartlist_add(lines, buf);
   }
   smartlist_sort_strings(lines);
 
diff --git a/src/or/dirvote.c b/src/or/dirvote.c
index 30e340c73599eb43cbf9458c23299ae9e0e81a53..bae222a191160b7593200a0ddb0860561b52187e 100644
--- a/src/or/dirvote.c
+++ b/src/or/dirvote.c
@@ -1187,7 +1187,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
   chunks = smartlist_create();
 
   {
-    char buf[1024];
+    char *buf=NULL;
     char va_buf[ISO_TIME_LEN+1], fu_buf[ISO_TIME_LEN+1],
       vu_buf[ISO_TIME_LEN+1];
     char *flaglist;
@@ -1196,20 +1196,20 @@ networkstatus_compute_consensus(smartlist_t *votes,
     format_iso_time(vu_buf, valid_until);
     flaglist = smartlist_join_strings(flags, " ", 0, NULL);
 
-    tor_snprintf(buf, sizeof(buf), "network-status-version 3%s%s\n"
+    tor_asprintf(&buf, "network-status-version 3%s%s\n"
                  "vote-status consensus\n",
                  flavor == FLAV_NS ? "" : " ",
                  flavor == FLAV_NS ? "" : flavor_name);
 
-    smartlist_add(chunks, tor_strdup(buf));
+    smartlist_add(chunks, buf);
 
     if (consensus_method >= 2) {
-      tor_snprintf(buf, sizeof(buf), "consensus-method %d\n",
+      tor_asprintf(&buf, "consensus-method %d\n",
                    consensus_method);
-      smartlist_add(chunks, tor_strdup(buf));
+      smartlist_add(chunks, buf);
     }
 
-    tor_snprintf(buf, sizeof(buf),
+    tor_asprintf(&buf,
                  "valid-after %s\n"
                  "fresh-until %s\n"
                  "valid-until %s\n"
@@ -1220,7 +1220,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
                  va_buf, fu_buf, vu_buf,
                  vote_seconds, dist_seconds,
                  client_versions, server_versions, flaglist);
-    smartlist_add(chunks, tor_strdup(buf));
+    smartlist_add(chunks, buf);
 
     tor_free(flaglist);
   }
@@ -1256,15 +1256,14 @@ networkstatus_compute_consensus(smartlist_t *votes,
     } SMARTLIST_FOREACH_END(v);
     smartlist_sort(dir_sources, _compare_dir_src_ents_by_authority_id);
 
-    SMARTLIST_FOREACH(dir_sources, const dir_src_ent_t *, e,
-    {
-      char buf[1024];
+    SMARTLIST_FOREACH_BEGIN(dir_sources, const dir_src_ent_t *, e) {
       struct in_addr in;
       char ip[INET_NTOA_BUF_LEN];
       char fingerprint[HEX_DIGEST_LEN+1];
       char votedigest[HEX_DIGEST_LEN+1];
       networkstatus_t *v = e->v;
       networkstatus_voter_info_t *voter = get_voter(v);
+      char *buf = NULL;
 
       if (e->is_legacy)
         tor_assert(consensus_method >= 2);
@@ -1275,22 +1274,22 @@ networkstatus_compute_consensus(smartlist_t *votes,
       base16_encode(votedigest, sizeof(votedigest), voter->vote_digest,
                     DIGEST_LEN);
 
-      tor_snprintf(buf, sizeof(buf),
+      tor_asprintf(&buf,
                    "dir-source %s%s %s %s %s %d %d\n",
                    voter->nickname, e->is_legacy ? "-legacy" : "",
                    fingerprint, voter->address, ip,
                    voter->dir_port,
                    voter->or_port);
-      smartlist_add(chunks, tor_strdup(buf));
+      smartlist_add(chunks, buf);
       if (! e->is_legacy) {
-        tor_snprintf(buf, sizeof(buf),
+        tor_asprintf(&buf,
                      "contact %s\n"
                      "vote-digest %s\n",
                      voter->contact,
                      votedigest);
-        smartlist_add(chunks, tor_strdup(buf));
+        smartlist_add(chunks, buf);
       }
-    });
+    } SMARTLIST_FOREACH_END(e);
     SMARTLIST_FOREACH(dir_sources, dir_src_ent_t *, e, tor_free(e));
     smartlist_free(dir_sources);
   }
@@ -1426,7 +1425,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
       int naming_conflict = 0;
       int n_listing = 0;
       int i;
-      char buf[256];
+      char *buf=NULL;
       char microdesc_digest[DIGEST256_LEN];
 
       /* Of the next-to-be-considered digest in each voter, which is first? */
@@ -1675,19 +1674,20 @@ networkstatus_compute_consensus(smartlist_t *votes,
         }
       }
 
-      /* Okay!! Now we can write the descriptor... */
-      /*     First line goes into "buf". */
-      routerstatus_format_entry(buf, sizeof(buf), &rs_out, NULL,
-                                rs_format);
-      smartlist_add(chunks, tor_strdup(buf));
+      {
+        char buf[4096];
+        /* Okay!! Now we can write the descriptor... */
+        /*     First line goes into "buf". */
+        routerstatus_format_entry(buf, sizeof(buf), &rs_out, NULL,
+                                  rs_format);
+        smartlist_add(chunks, tor_strdup(buf));
+      }
       /*     Now an m line, if applicable. */
       if (flavor == FLAV_MICRODESC &&
           !tor_digest256_is_zero(microdesc_digest)) {
         char m[BASE64_DIGEST256_LEN+1], *cp;
-        const size_t mlen = BASE64_DIGEST256_LEN+5;
         digest256_to_base64(m, microdesc_digest);
-        cp = tor_malloc(mlen);
-        tor_snprintf(cp, mlen, "m %s\n", m);
+        tor_asprintf(&cp, "m %s\n", m);
         smartlist_add(chunks, cp);
       }
       /*     Next line is all flags.  The "\n" is missing. */
@@ -1701,26 +1701,16 @@ networkstatus_compute_consensus(smartlist_t *votes,
       smartlist_add(chunks, tor_strdup("\n"));
       /*     Now the weight line. */
       if (rs_out.has_bandwidth) {
-        int r = tor_snprintf(buf, sizeof(buf),
-                             "w Bandwidth=%d\n", rs_out.bandwidth);
-        if (r<0) {
-          log_warn(LD_BUG, "Not enough space in buffer for weight line.");
-          *buf = '\0';
-        }
-
-        smartlist_add(chunks, tor_strdup(buf));
-      };
+        char *cp=NULL;
+        tor_asprintf(&cp, "w Bandwidth=%d\n", rs_out.bandwidth);
+        smartlist_add(chunks, cp);
+      }
 
       /*     Now the exitpolicy summary line. */
       if (rs_out.has_exitsummary && flavor == FLAV_NS) {
-        char buf[MAX_POLICY_LINE_LEN+1];
-        int r = tor_snprintf(buf, sizeof(buf), "p %s\n", rs_out.exitsummary);
-        if (r<0) {
-          log_warn(LD_BUG, "Not enough space in buffer for exitpolicy line.");
-          *buf = '\0';
-        }
-        smartlist_add(chunks, tor_strdup(buf));
-      };
+        tor_asprintf(&buf, "p %s\n", rs_out.exitsummary);
+        smartlist_add(chunks, buf);
+      }
 
       /* And the loop is over and we move on to the next router */
     }
@@ -1795,8 +1785,9 @@ networkstatus_compute_consensus(smartlist_t *votes,
     size_t digest_len =
       flavor == FLAV_NS ? DIGEST_LEN : DIGEST256_LEN;
     const char *algname = crypto_digest_algorithm_get_name(digest_alg);
+    char *buf = NULL;
+    char sigbuf[4096];
 
-    char buf[4096];
     smartlist_add(chunks, tor_strdup("directory-signature "));
 
     /* Compute the hash of the chunks. */
@@ -1808,20 +1799,23 @@ networkstatus_compute_consensus(smartlist_t *votes,
 
     /* add the junk that will go at the end of the line. */
     if (flavor == FLAV_NS) {
-      tor_snprintf(buf, sizeof(buf), "%s %s\n", fingerprint,
+      tor_asprintf(&buf, "%s %s\n", fingerprint,
                    signing_key_fingerprint);
     } else {
-      tor_snprintf(buf, sizeof(buf), "%s %s %s\n",
+      tor_asprintf(&buf, "%s %s %s\n",
                    algname, fingerprint,
                    signing_key_fingerprint);
     }
+    smartlist_add(chunks, buf);
     /* And the signature. */
-    if (router_append_dirobj_signature(buf, sizeof(buf), digest, digest_len,
+    sigbuf[0] = '\0';
+    if (router_append_dirobj_signature(sigbuf, sizeof(sigbuf),
+                                       digest, digest_len,
                                        signing_key)) {
       log_warn(LD_BUG, "Couldn't sign consensus networkstatus.");
       return NULL; /* This leaks, but it should never happen. */
     }
-    smartlist_add(chunks, tor_strdup(buf));
+    smartlist_add(chunks, tor_strdup(sigbuf));
 
     if (legacy_id_key_digest && legacy_signing_key && consensus_method >= 3) {
       smartlist_add(chunks, tor_strdup("directory-signature "));
@@ -1830,19 +1824,22 @@ networkstatus_compute_consensus(smartlist_t *votes,
       crypto_pk_get_fingerprint(legacy_signing_key,
                                 signing_key_fingerprint, 0);
       if (flavor == FLAV_NS) {
-        tor_snprintf(buf, sizeof(buf), "%s %s\n", fingerprint,
+        tor_asprintf(&buf, "%s %s\n", fingerprint,
                      signing_key_fingerprint);
       } else {
-        tor_snprintf(buf, sizeof(buf), "%s %s %s\n",
+        tor_asprintf(&buf, "%s %s %s\n",
                      algname, fingerprint,
                      signing_key_fingerprint);
       }
-      if (router_append_dirobj_signature(buf, sizeof(buf), digest, digest_len,
+      smartlist_add(chunks, buf);
+      sigbuf[0] = '\0';
+      if (router_append_dirobj_signature(sigbuf, sizeof(sigbuf),
+                                         digest, digest_len,
                                          legacy_signing_key)) {
         log_warn(LD_BUG, "Couldn't sign consensus networkstatus.");
         return NULL; /* This leaks, but it should never happen. */
       }
-      smartlist_add(chunks, tor_strdup(buf));
+      smartlist_add(chunks, tor_strdup(sigbuf));
     }
   }
 
diff --git a/src/or/geoip.c b/src/or/geoip.c
index 0f4805ec9d0e8bf7c3dfa2942ad9a4a6734497d0..b5a0374c492b1be6617a032b8ff0ae6b9fac723d 100644
--- a/src/or/geoip.c
+++ b/src/or/geoip.c
@@ -815,7 +815,6 @@ geoip_get_client_history(time_t now, geoip_client_action_t action,
   if (!geoip_is_loaded())
     return NULL;
   if (client_history_starts < (now - min_observation_time)) {
-    char buf[32];
     smartlist_t *chunks = NULL;
     smartlist_t *entries = NULL;
     int n_countries = geoip_get_n_countries();
@@ -860,9 +859,10 @@ geoip_get_client_history(time_t now, geoip_client_action_t action,
     /* Build the result. */
     chunks = smartlist_create();
     SMARTLIST_FOREACH(entries, c_hist_t *, ch, {
-        tor_snprintf(buf, sizeof(buf), "%s=%u", ch->country, ch->total);
-        smartlist_add(chunks, tor_strdup(buf));
-      });
+        char *buf=NULL;
+        tor_asprintf(&buf, "%s=%u", ch->country, ch->total);
+        smartlist_add(chunks, buf);
+    });
     result = smartlist_join_strings(chunks, ",", 0, NULL);
   done:
     tor_free(counts);
@@ -947,7 +947,7 @@ geoip_get_request_history(time_t now, geoip_client_action_t action)
   SMARTLIST_FOREACH(entries, c_hist_t *, ent, {
       char buf[32];
       tor_snprintf(buf, sizeof(buf), "%s=%u", ent->country, ent->total);
-      smartlist_add(strings, tor_strdup(buf));
+      smartlist_add(strings, buf);
     });
   result = smartlist_join_strings(strings, ",", 0, NULL);
   SMARTLIST_FOREACH(strings, char *, cp, tor_free(cp));
@@ -1105,7 +1105,6 @@ parse_bridge_stats_controller(const char *stats_str, time_t now)
   const char *BRIDGE_IPS_EMPTY_LINE = "bridge-ips\n";
   const char *tmp;
   time_t stats_end_time;
-  size_t controller_len;
   int seconds;
   tor_assert(stats_str);
 
@@ -1147,16 +1146,9 @@ parse_bridge_stats_controller(const char *stats_str, time_t now)
     summary = tor_strdup("");
   }
 
-  controller_len = strlen("TimeStarted=\"\" CountrySummary=") +
-                          strlen(summary) + 42;
-  controller_str = tor_malloc(controller_len);
-  if (tor_snprintf(controller_str, controller_len,
-                   "TimeStarted=\"%s\" CountrySummary=%s",
-                   stats_start_str, summary) < 0) {
-    tor_free(controller_str);
-    tor_free(summary);
-    return NULL;
-  }
+  tor_asprintf(&controller_str,
+               "TimeStarted=\"%s\" CountrySummary=%s",
+               stats_start_str, summary);
   tor_free(summary);
   return controller_str;
 }
diff --git a/src/or/rephist.c b/src/or/rephist.c
index c8da8dfe2d4fc56bd908f8553c8e1bfe007b0eb6..0e55db2d636e8ce6f1be98c9cbc8e0ec6f9494e8 100644
--- a/src/or/rephist.c
+++ b/src/or/rephist.c
@@ -793,12 +793,12 @@ rep_hist_record_mtbf_data(time_t now, int missing_means_down)
 static char *
 rep_hist_format_router_status(or_history_t *hist, time_t now)
 {
-  char buf[1024];
   char sor_buf[ISO_TIME_LEN+1];
   char sod_buf[ISO_TIME_LEN+1];
   double wfu;
   double mtbf;
   int up = 0, down = 0;
+  char *cp = NULL;
 
   if (hist->start_of_run) {
     format_iso_time(sor_buf, hist->start_of_run);
@@ -811,7 +811,7 @@ rep_hist_format_router_status(or_history_t *hist, time_t now)
 
   wfu = get_weighted_fractional_uptime(hist, now);
   mtbf = get_stability(hist, now);
-  tor_snprintf(buf, sizeof(buf),
+  tor_asprintf(&cp,
                "%s%s%s"
                "%s%s%s"
                "wfu %0.3lf\n"
@@ -829,8 +829,7 @@ rep_hist_format_router_status(or_history_t *hist, time_t now)
                hist->weighted_run_length,
                hist->total_run_weights
                );
-
-  return tor_strdup(buf);
+  return cp;
 }
 
 /** The last stability analysis document that we created, or NULL if we never
@@ -2140,8 +2139,7 @@ rep_hist_buffer_stats_write(time_t now)
       number_of_circuits, i;
   double queued_cells[SHARES], time_in_queue[SHARES];
   smartlist_t *str_build = smartlist_create();
-  char *str = NULL;
-  char buf[32];
+  char *str = NULL, *buf=NULL;
   circuit_t *circ;
   /* add current circuits to stats */
   for (circ = _circuit_get_global_list(); circ; circ = circ->next)
@@ -2190,9 +2188,9 @@ rep_hist_buffer_stats_write(time_t now)
               (unsigned) (now - start_of_buffer_stats_interval)) < 0)
     goto done;
   for (i = 0; i < SHARES; i++) {
-    tor_snprintf(buf, sizeof(buf), "%d", !circs_in_share[i] ? 0 :
+    tor_asprintf(&buf,"%d", !circs_in_share[i] ? 0 :
                  processed_cells[i] / circs_in_share[i]);
-    smartlist_add(str_build, tor_strdup(buf));
+    smartlist_add(str_build, buf);
   }
   str = smartlist_join_strings(str_build, ",", 0, NULL);
   if (fprintf(out, "cell-processed-cells %s\n", str) < 0)
@@ -2201,9 +2199,9 @@ rep_hist_buffer_stats_write(time_t now)
   SMARTLIST_FOREACH(str_build, char *, c, tor_free(c));
   smartlist_clear(str_build);
   for (i = 0; i < SHARES; i++) {
-    tor_snprintf(buf, sizeof(buf), "%.2f", circs_in_share[i] == 0 ? 0.0 :
+    tor_asprintf(&buf, "%.2f", circs_in_share[i] == 0 ? 0.0 :
                  queued_cells[i] / (double) circs_in_share[i]);
-    smartlist_add(str_build, tor_strdup(buf));
+    smartlist_add(str_build, buf);
   }
   str = smartlist_join_strings(str_build, ",", 0, NULL);
   if (fprintf(out, "cell-queued-cells %s\n", str) < 0)
@@ -2212,9 +2210,9 @@ rep_hist_buffer_stats_write(time_t now)
   SMARTLIST_FOREACH(str_build, char *, c, tor_free(c));
   smartlist_clear(str_build);
   for (i = 0; i < SHARES; i++) {
-    tor_snprintf(buf, sizeof(buf), "%.0f", circs_in_share[i] == 0 ? 0.0 :
+    tor_asprintf(&buf, "%.0f", circs_in_share[i] == 0 ? 0.0 :
                  time_in_queue[i] / (double) circs_in_share[i]);
-    smartlist_add(str_build, tor_strdup(buf));
+    smartlist_add(str_build, buf);
   }
   str = smartlist_join_strings(str_build, ",", 0, NULL);
   if (fprintf(out, "cell-time-in-queue %s\n", str) < 0)
diff --git a/src/test/test_util.c b/src/test/test_util.c
index 34a6f4d66216a13d8ce9e32ea1ca8ba924e8e4e9..ad5a27e517ac23d26778e551b111a8272f88a1b8 100644
--- a/src/test/test_util.c
+++ b/src/test/test_util.c
@@ -1050,6 +1050,51 @@ test_util_find_str_at_start_of_line(void *ptr)
   ;
 }
 
+static void
+test_util_asprintf(void *ptr)
+{
+#define LOREMIPSUM                                              \
+  "Lorem ipsum dolor sit amet, consectetur adipisicing elit"
+  char *cp=NULL, *cp2=NULL;
+  int r;
+  (void)ptr;
+
+  /* empty string. */
+  r = tor_asprintf(&cp, "%s", "");
+  tt_assert(cp);
+  tt_int_op(r, ==, strlen(cp));
+  tt_str_op(cp, ==, "");
+
+  /* Short string with some printing in it. */
+  r = tor_asprintf(&cp2, "First=%d, Second=%d", 101, 202);
+  tt_assert(cp2);
+  tt_int_op(r, ==, strlen(cp2));
+  tt_str_op(cp2, ==, "First=101, Second=202");
+  tt_assert(cp != cp2);
+  tor_free(cp);
+  tor_free(cp2);
+
+  /* Glass-box test: a string exactly 128 characters long. */
+  r = tor_asprintf(&cp, "Lorem1: %sLorem2: %s", LOREMIPSUM, LOREMIPSUM);
+  tt_assert(cp);
+  tt_int_op(r, ==, 128);
+  tt_assert(cp[128] == '\0');
+  tt_str_op(cp, ==,
+            "Lorem1: "LOREMIPSUM"Lorem2: "LOREMIPSUM);
+  tor_free(cp);
+
+  /* String longer than 128 characters */
+  r = tor_asprintf(&cp, "1: %s 2: %s 3: %s",
+                   LOREMIPSUM, LOREMIPSUM, LOREMIPSUM);
+  tt_assert(cp);
+  tt_int_op(r, ==, strlen(cp));
+  tt_str_op(cp, ==, "1: "LOREMIPSUM" 2: "LOREMIPSUM" 3: "LOREMIPSUM);
+
+ done:
+  tor_free(cp);
+  tor_free(cp2);
+}
+
 #define UTIL_LEGACY(name)                                               \
   { #name, legacy_test_helper, 0, &legacy_setup, test_util_ ## name }
 
@@ -1071,6 +1116,7 @@ struct testcase_t util_tests[] = {
   UTIL_LEGACY(sscanf),
   UTIL_LEGACY(strtok),
   UTIL_TEST(find_str_at_start_of_line, 0),
+  UTIL_TEST(asprintf, 0),
   END_OF_TESTCASES
 };