Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit36147ec

Browse files
committed
Make snprintf.c follow the C99 standard for snprintf's result value.
C99 says that the result should be the number of bytes that would havebeen emitted given a large enough buffer, not the number we actuallywere able to put in the buffer. It's time to make our substituteimplementation comply with that. Not doing so results in inefficiencyin buffer-enlargement cases, and also poses a portability hazard forthird-party code that might expect C99-compliant snprintf behaviorwithin Postgres.In passing, remove useless tests for str == NULL; neither C99 norpredecessor standards ever allowed that except when count == 0,so I see no reason to expend cycles on making that a non-crash casefor this implementation. Also, don't waste a byte in pg_vfprintf'slocal I/O buffer; this might have performance benefits by allowingaligned writes during flushbuffer calls.Back-patch of commit805889d. There was some concern about thispossibly breaking code that assumes pre-C99 behavior, but there ismuch more risk (and reality, in our own code) of code that assumesC99 behavior and hence fails to detect buffer overrun without this.Discussion:https://postgr.es/m/17245.1534289329@sss.pgh.pa.us
1 parent6872c2b commit36147ec

File tree

1 file changed

+63
-31
lines changed

1 file changed

+63
-31
lines changed

‎src/port/snprintf.c

Lines changed: 63 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* Copyright (c) 1983, 1995, 1996 Eric P. Allman
33
* Copyright (c) 1988, 1993
44
*The Regents of the University of California. All rights reserved.
5+
* Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
56
*
67
* Redistribution and use in source and binary forms, with or without
78
* modification, are permitted provided that the following conditions
@@ -52,8 +53,8 @@
5253
*SNPRINTF, VSNPRINTF and friends
5354
*
5455
* These versions have been grabbed off the net. They have been
55-
* cleaned up to compile properly and support for most of theSingle Unix
56-
*Specification has been added. Remaining unimplemented features are:
56+
* cleaned up to compile properly and support for most of theC99
57+
*specification has been added. Remaining unimplemented features are:
5758
*
5859
* 1. No locale support: the radix character is always '.' and the '
5960
* (single quote) format flag is ignored.
@@ -67,25 +68,24 @@
6768
* 5. Space and '#' flags are not implemented.
6869
*
6970
*
70-
*Theresult values ofthese functions are not the sameacrossdifferent
71-
*platforms.This implementationis compatible withtheSingle Unix Spec:
71+
*Historically theresult values ofsprintf/snprintf variedacrossplatforms.
72+
* This implementationnow followstheC99 standard:
7273
*
73-
* 1. -1 is returned only if processing is abandoned due to an invalid
74-
* parameter, such as incorrect format string. (Although not required by
75-
* the spec, this happens only when no characters have yet been transmitted
76-
* to the destination.)
74+
* 1. -1 is returned if an error is detected in the format string, or if
75+
* a write to the target stream fails (as reported by fwrite). Note that
76+
* overrunning snprintf's target buffer is *not* an error.
7777
*
78-
* 2. Forsnprintf and sprintf, 0 is returned if str == NULL or count == 0;
79-
*no data has been stored.
78+
* 2. Forsuccessful writes to streams, the actual number of bytes written
79+
*to the stream is returned.
8080
*
81-
* 3. Otherwise, the number of bytes actually transmitted to the destination
82-
* is returned (excluding the trailing '\0' for snprintf and sprintf).
81+
* 3. For successful sprintf/snprintf, the number of bytes that would have
82+
* been written to an infinite-size buffer (excluding the trailing '\0')
83+
* is returned. snprintf will truncate its output to fit in the buffer
84+
* (ensuring a trailing '\0' unless count == 0), but this is not reflected
85+
* in the function result.
8386
*
84-
* For snprintf with nonzero count, the result cannot be more than count-1
85-
* (a trailing '\0' is always stored); it is not possible to distinguish
86-
* buffer overrun from exact fit. This is unlike some implementations that
87-
* return the number of bytes that would have been needed for the complete
88-
* result string.
87+
* snprintf buffer overrun can be detected by checking for function result
88+
* greater than or equal to the supplied count.
8989
*/
9090

9191
/**************************************************************
@@ -104,15 +104,27 @@
104104
#undeffprintf
105105
#undefprintf
106106

107-
/* Info about where the formatted output is going */
107+
/*
108+
* Info about where the formatted output is going.
109+
*
110+
* dopr and subroutines will not write at/past bufend, but snprintf
111+
* reserves one byte, ensuring it may place the trailing '\0' there.
112+
*
113+
* In snprintf, we use nchars to count the number of bytes dropped on the
114+
* floor due to buffer overrun. The correct result of snprintf is thus
115+
* (bufptr - bufstart) + nchars. (This isn't as inconsistent as it might
116+
* seem: nchars is the number of emitted bytes that are not in the buffer now,
117+
* either because we sent them to the stream or because we couldn't fit them
118+
* into the buffer to begin with.)
119+
*/
108120
typedefstruct
109121
{
110122
char*bufptr;/* next buffer output position */
111123
char*bufstart;/* first buffer element */
112-
char*bufend;/* last buffer element, or NULL */
124+
char*bufend;/* last+1 buffer element, or NULL */
113125
/* bufend == NULL is for sprintf, where we assume buf is big enough */
114126
FILE*stream;/* eventual output destination, or NULL */
115-
intnchars;/* # charsalreadysent to stream */
127+
intnchars;/* # chars sent to stream, or dropped */
116128
boolfailed;/* call is a failure; errno is set */
117129
}PrintfTarget;
118130

@@ -150,17 +162,28 @@ int
150162
pg_vsnprintf(char*str,size_tcount,constchar*fmt,va_listargs)
151163
{
152164
PrintfTargettarget;
165+
charonebyte[1];
153166

154-
if (str==NULL||count==0)
155-
return0;
167+
/*
168+
* C99 allows the case str == NULL when count == 0. Rather than
169+
* special-casing this situation further down, we substitute a one-byte
170+
* local buffer. Callers cannot tell, since the function result doesn't
171+
* depend on count.
172+
*/
173+
if (count==0)
174+
{
175+
str=onebyte;
176+
count=1;
177+
}
156178
target.bufstart=target.bufptr=str;
157179
target.bufend=str+count-1;
158180
target.stream=NULL;
159-
/*target.ncharsis unused in this case */
181+
target.nchars=0;
160182
target.failed= false;
161183
dopr(&target,fmt,args);
162184
*(target.bufptr)='\0';
163-
returntarget.failed ?-1 : (target.bufptr-target.bufstart);
185+
returntarget.failed ?-1 : (target.bufptr-target.bufstart
186+
+target.nchars);
164187
}
165188

166189
int
@@ -180,16 +203,15 @@ pg_vsprintf(char *str, const char *fmt, va_list args)
180203
{
181204
PrintfTargettarget;
182205

183-
if (str==NULL)
184-
return0;
185206
target.bufstart=target.bufptr=str;
186207
target.bufend=NULL;
187208
target.stream=NULL;
188-
/*target.ncharsis unused in this case */
209+
target.nchars=0;/* not really used in this case */
189210
target.failed= false;
190211
dopr(&target,fmt,args);
191212
*(target.bufptr)='\0';
192-
returntarget.failed ?-1 : (target.bufptr-target.bufstart);
213+
returntarget.failed ?-1 : (target.bufptr-target.bufstart
214+
+target.nchars);
193215
}
194216

195217
int
@@ -216,7 +238,7 @@ pg_vfprintf(FILE *stream, const char *fmt, va_list args)
216238
return-1;
217239
}
218240
target.bufstart=target.bufptr=buffer;
219-
target.bufend=buffer+sizeof(buffer)-1;
241+
target.bufend=buffer+sizeof(buffer);/* use the whole buffer */
220242
target.stream=stream;
221243
target.nchars=0;
222244
target.failed= false;
@@ -259,6 +281,10 @@ flushbuffer(PrintfTarget *target)
259281
{
260282
size_tnc=target->bufptr-target->bufstart;
261283

284+
/*
285+
* Don't write anything if we already failed; this is to ensure we
286+
* preserve the original failure's errno.
287+
*/
262288
if (!target->failed&&nc>0)
263289
{
264290
size_twritten;
@@ -1038,7 +1064,10 @@ dostr(const char *str, int slen, PrintfTarget *target)
10381064
{
10391065
/* buffer full, can we dump to stream? */
10401066
if (target->stream==NULL)
1041-
return;/* no, lose the data */
1067+
{
1068+
target->nchars+=slen;/* no, lose the data */
1069+
return;
1070+
}
10421071
flushbuffer(target);
10431072
continue;
10441073
}
@@ -1057,7 +1086,10 @@ dopr_outch(int c, PrintfTarget *target)
10571086
{
10581087
/* buffer full, can we dump to stream? */
10591088
if (target->stream==NULL)
1060-
return;/* no, lose the data */
1089+
{
1090+
target->nchars++;/* no, lose the data */
1091+
return;
1092+
}
10611093
flushbuffer(target);
10621094
}
10631095
*(target->bufptr++)=c;

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp