1 /*--------------------------------------------------------------------
2  * Symbols referenced in this file:
3  * - pvsnprintf
4  * - psprintf
5  *--------------------------------------------------------------------
6  */
7 
8 /*-------------------------------------------------------------------------
9  *
10  * psprintf.c
11  *		sprintf into an allocated-on-demand buffer
12  *
13  *
14  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
15  * Portions Copyright (c) 1994, Regents of the University of California
16  *
17  *
18  * IDENTIFICATION
19  *	  src/common/psprintf.c
20  *
21  *-------------------------------------------------------------------------
22  */
23 
24 #ifndef FRONTEND
25 
26 #include "postgres.h"
27 
28 #include "utils/memutils.h"
29 
30 #else
31 
32 #include "postgres_fe.h"
33 
34 /* It's possible we could use a different value for this in frontend code */
35 #define MaxAllocSize	((Size) 0x3fffffff) /* 1 gigabyte - 1 */
36 
37 #endif
38 
39 
40 /*
41  * psprintf
42  *
43  * Format text data under the control of fmt (an sprintf-style format string)
44  * and return it in an allocated-on-demand buffer.  The buffer is allocated
45  * with palloc in the backend, or malloc in frontend builds.  Caller is
46  * responsible to free the buffer when no longer needed, if appropriate.
47  *
48  * Errors are not returned to the caller, but are reported via elog(ERROR)
49  * in the backend, or printf-to-stderr-and-exit() in frontend builds.
50  * One should therefore think twice about using this in libpq.
51  */
52 char *
psprintf(const char * fmt,...)53 psprintf(const char *fmt,...)
54 {
55 	size_t		len = 128;		/* initial assumption about buffer size */
56 
57 	for (;;)
58 	{
59 		char	   *result;
60 		va_list		args;
61 		size_t		newlen;
62 
63 		/*
64 		 * Allocate result buffer.  Note that in frontend this maps to malloc
65 		 * with exit-on-error.
66 		 */
67 		result = (char *) palloc(len);
68 
69 		/* Try to format the data. */
70 		va_start(args, fmt);
71 		newlen = pvsnprintf(result, len, fmt, args);
72 		va_end(args);
73 
74 		if (newlen < len)
75 			return result;		/* success */
76 
77 		/* Release buffer and loop around to try again with larger len. */
78 		pfree(result);
79 		len = newlen;
80 	}
81 }
82 
83 /*
84  * pvsnprintf
85  *
86  * Attempt to format text data under the control of fmt (an sprintf-style
87  * format string) and insert it into buf (which has length len, len > 0).
88  *
89  * If successful, return the number of bytes emitted, not counting the
90  * trailing zero byte.  This will always be strictly less than len.
91  *
92  * If there's not enough space in buf, return an estimate of the buffer size
93  * needed to succeed (this *must* be more than the given len, else callers
94  * might loop infinitely).
95  *
96  * Other error cases do not return, but exit via elog(ERROR) or exit().
97  * Hence, this shouldn't be used inside libpq.
98  *
99  * This function exists mainly to centralize our workarounds for
100  * non-C99-compliant vsnprintf implementations.  Generally, any call that
101  * pays any attention to the return value should go through here rather
102  * than calling snprintf or vsnprintf directly.
103  *
104  * Note that the semantics of the return value are not exactly C99's.
105  * First, we don't promise that the estimated buffer size is exactly right;
106  * callers must be prepared to loop multiple times to get the right size.
107  * Second, we return the recommended buffer size, not one less than that;
108  * this lets overflow concerns be handled here rather than in the callers.
109  */
110 size_t
pvsnprintf(char * buf,size_t len,const char * fmt,va_list args)111 pvsnprintf(char *buf, size_t len, const char *fmt, va_list args)
112 {
113 	int			nprinted;
114 
115 	Assert(len > 0);
116 
117 	errno = 0;
118 
119 	/*
120 	 * Assert check here is to catch buggy vsnprintf that overruns the
121 	 * specified buffer length.  Solaris 7 in 64-bit mode is an example of a
122 	 * platform with such a bug.
123 	 */
124 #ifdef USE_ASSERT_CHECKING
125 	buf[len - 1] = '\0';
126 #endif
127 
128 	nprinted = vsnprintf(buf, len, fmt, args);
129 
130 	Assert(buf[len - 1] == '\0');
131 
132 	/*
133 	 * If vsnprintf reports an error other than ENOMEM, fail.  The possible
134 	 * causes of this are not user-facing errors, so elog should be enough.
135 	 */
136 	if (nprinted < 0 && errno != 0 && errno != ENOMEM)
137 	{
138 #ifndef FRONTEND
139 		elog(ERROR, "vsnprintf failed: %m");
140 #else
141 		fprintf(stderr, "vsnprintf failed: %s\n", strerror(errno));
142 		exit(EXIT_FAILURE);
143 #endif
144 	}
145 
146 	/*
147 	 * Note: some versions of vsnprintf return the number of chars actually
148 	 * stored, not the total space needed as C99 specifies.  And at least one
149 	 * returns -1 on failure.  Be conservative about believing whether the
150 	 * print worked.
151 	 */
152 	if (nprinted >= 0 && (size_t) nprinted < len - 1)
153 	{
154 		/* Success.  Note nprinted does not include trailing null. */
155 		return (size_t) nprinted;
156 	}
157 
158 	if (nprinted >= 0 && (size_t) nprinted > len)
159 	{
160 		/*
161 		 * This appears to be a C99-compliant vsnprintf, so believe its
162 		 * estimate of the required space.  (If it's wrong, the logic will
163 		 * still work, but we may loop multiple times.)  Note that the space
164 		 * needed should be only nprinted+1 bytes, but we'd better allocate
165 		 * one more than that so that the test above will succeed next time.
166 		 *
167 		 * In the corner case where the required space just barely overflows,
168 		 * fall through so that we'll error out below (possibly after
169 		 * looping).
170 		 */
171 		if ((size_t) nprinted <= MaxAllocSize - 2)
172 			return nprinted + 2;
173 	}
174 
175 	/*
176 	 * Buffer overrun, and we don't know how much space is needed.  Estimate
177 	 * twice the previous buffer size, but not more than MaxAllocSize; if we
178 	 * are already at MaxAllocSize, choke.  Note we use this palloc-oriented
179 	 * overflow limit even when in frontend.
180 	 */
181 	if (len >= MaxAllocSize)
182 	{
183 #ifndef FRONTEND
184 		ereport(ERROR,
185 				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
186 				 errmsg("out of memory")));
187 #else
188 		fprintf(stderr, _("out of memory\n"));
189 		exit(EXIT_FAILURE);
190 #endif
191 	}
192 
193 	if (len >= MaxAllocSize / 2)
194 		return MaxAllocSize;
195 
196 	return len * 2;
197 }
198