1 /* Copyright (c) 2003-2004, Roger Dingledine
2  * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3  * Copyright (c) 2007-2021, The Tor Project, Inc. */
4 /* See LICENSE for licensing information */
5 
6 /**
7  * \file printf.c
8  * \brief Compatibility wrappers around snprintf and its friends
9  **/
10 
11 #include "lib/string/printf.h"
12 #include "lib/err/torerr.h"
13 #include "lib/cc/torint.h"
14 #include "lib/malloc/malloc.h"
15 
16 #include <stdlib.h>
17 #include <stdio.h>
18 
19 /** Replacement for snprintf.  Differs from platform snprintf in two
20  * ways: First, always NUL-terminates its output.  Second, always
21  * returns -1 if the result is truncated.  (Note that this return
22  * behavior does <i>not</i> conform to C99; it just happens to be
23  * easier to emulate "return -1" with conformant implementations than
24  * it is to emulate "return number that would be written" with
25  * non-conformant implementations.) */
26 int
tor_snprintf(char * str,size_t size,const char * format,...)27 tor_snprintf(char *str, size_t size, const char *format, ...)
28 {
29   va_list ap;
30   int r;
31   va_start(ap,format);
32   r = tor_vsnprintf(str,size,format,ap);
33   va_end(ap);
34   return r;
35 }
36 
37 /** Replacement for vsnprintf; behavior differs as tor_snprintf differs from
38  * snprintf.
39  */
40 int
tor_vsnprintf(char * str,size_t size,const char * format,va_list args)41 tor_vsnprintf(char *str, size_t size, const char *format, va_list args)
42 {
43   int r;
44   if (size == 0)
45     return -1; /* no place for the NUL */
46   if (size > SIZE_T_CEILING)
47     return -1;
48 #ifdef _WIN32
49   r = _vsnprintf(str, size, format, args);
50 #else
51   r = vsnprintf(str, size, format, args);
52 #endif
53   str[size-1] = '\0';
54   if (r < 0 || r >= (ssize_t)size)
55     return -1;
56   return r;
57 }
58 
59 /**
60  * Portable asprintf implementation.  Does a printf() into a newly malloc'd
61  * string.  Sets *<b>strp</b> to this string, and returns its length (not
62  * including the terminating NUL character).
63  *
64  * You can treat this function as if its implementation were something like
65    <pre>
66      char buf[_INFINITY_];
67      tor_snprintf(buf, sizeof(buf), fmt, args);
68      *strp = tor_strdup(buf);
69      return strlen(*strp):
70    </pre>
71  * Where _INFINITY_ is an imaginary constant so big that any string can fit
72  * into it.
73  */
74 int
tor_asprintf(char ** strp,const char * fmt,...)75 tor_asprintf(char **strp, const char *fmt, ...)
76 {
77   int r;
78   va_list args;
79   va_start(args, fmt);
80   r = tor_vasprintf(strp, fmt, args);
81   va_end(args);
82   if (!*strp || r < 0) {
83     /* LCOV_EXCL_START */
84     raw_assert_unreached_msg("Internal error in asprintf");
85     /* LCOV_EXCL_STOP */
86   }
87   return r;
88 }
89 
90 /**
91  * Portable vasprintf implementation.  Does a printf() into a newly malloc'd
92  * string.  Differs from regular vasprintf in the same ways that
93  * tor_asprintf() differs from regular asprintf.
94  */
95 int
tor_vasprintf(char ** strp,const char * fmt,va_list args)96 tor_vasprintf(char **strp, const char *fmt, va_list args)
97 {
98   /* use a temporary variable in case *strp is in args. */
99   char *strp_tmp=NULL;
100 #ifdef HAVE_VASPRINTF
101   /* If the platform gives us one, use it. */
102   int r = vasprintf(&strp_tmp, fmt, args);
103   if (r < 0)
104     *strp = NULL; // LCOV_EXCL_LINE -- no cross-platform way to force this
105   else
106     *strp = strp_tmp;
107   return r;
108 #elif defined(HAVE__VSCPRINTF)
109   /* On Windows, _vsnprintf won't tell us the length of the string if it
110    * overflows, so we need to use _vcsprintf to tell how much to allocate */
111   int len, r;
112   va_list tmp_args;
113   va_copy(tmp_args, args);
114   len = _vscprintf(fmt, tmp_args);
115   va_end(tmp_args);
116   if (len < 0) {
117     *strp = NULL;
118     return -1;
119   }
120   strp_tmp = tor_malloc((size_t)len + 1);
121   r = _vsnprintf(strp_tmp, (size_t)len+1, fmt, args);
122   if (r != len) {
123     tor_free(strp_tmp);
124     *strp = NULL;
125     return -1;
126   }
127   *strp = strp_tmp;
128   return len;
129 #else
130   /* Everywhere else, we have a decent vsnprintf that tells us how many
131    * characters we need.  We give it a try on a short buffer first, since
132    * it might be nice to avoid the second vsnprintf call.
133    */
134   /* XXXX This code spent a number of years broken (see bug 30651). It is
135    * possible that no Tor users actually run on systems without vasprintf() or
136    * _vscprintf(). If so, we should consider removing this code. */
137   char buf[128];
138   int len, r;
139   va_list tmp_args;
140   va_copy(tmp_args, args);
141   /* Use vsnprintf to retrieve needed length.  tor_vsnprintf() is not an
142    * option here because it will simply return -1 if buf is not large enough
143    * to hold the complete string.
144    */
145   len = vsnprintf(buf, sizeof(buf), fmt, tmp_args);
146   va_end(tmp_args);
147   buf[sizeof(buf) - 1] = '\0';
148   if (len < 0) {
149     *strp = NULL;
150     return -1;
151   }
152   if (len < (int)sizeof(buf)) {
153     *strp = tor_strdup(buf);
154     return len;
155   }
156   strp_tmp = tor_malloc((size_t)len+1);
157   /* use of tor_vsnprintf() will ensure string is null terminated */
158   r = tor_vsnprintf(strp_tmp, (size_t)len+1, fmt, args);
159   if (r != len) {
160     tor_free(strp_tmp);
161     *strp = NULL;
162     return -1;
163   }
164   *strp = strp_tmp;
165   return len;
166 #endif /* defined(HAVE_VASPRINTF) || ... */
167 }
168