Skip to content

Commit 54cd4f0

Browse files
committed
Work around a subtle portability problem in use of printf %s format.
Depending on which spec you read, field widths and precisions in %s may be counted either in bytes or characters. Our code was assuming bytes, which is wrong at least for glibc's implementation, and in any case libc might have a different idea of the prevailing encoding than we do. Hence, for portable results we must avoid using anything more complex than just "%s" unless the string to be printed is known to be all-ASCII. This patch fixes the cases I could find, including the psql formatting failure reported by Hernan Gonzalez. In HEAD only, I also added comments to some places where it appears safe to continue using "%.*s".
1 parent 71a185a commit 54cd4f0

File tree

11 files changed

+111
-31
lines changed

11 files changed

+111
-31
lines changed

src/backend/lib/stringinfo.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
1010
* Portions Copyright (c) 1994, Regents of the University of California
1111
*
12-
* $PostgreSQL: pgsql/src/backend/lib/stringinfo.c,v 1.52 2010/01/02 16:57:45 momjian Exp $
12+
* $PostgreSQL: pgsql/src/backend/lib/stringinfo.c,v 1.53 2010/05/08 16:39:49 tgl Exp $
1313
*
1414
*-------------------------------------------------------------------------
1515
*/
@@ -226,7 +226,8 @@ appendBinaryStringInfo(StringInfo str, const char *data, int datalen)
226226

227227
/*
228228
* Keep a trailing null in place, even though it's probably useless for
229-
* binary data...
229+
* binary data. (Some callers are dealing with text but call this
230+
* because their input isn't null-terminated.)
230231
*/
231232
str->data[str->len] = '\0';
232233
}

src/backend/parser/scansup.c

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
*
1010
*
1111
* IDENTIFICATION
12-
* $PostgreSQL: pgsql/src/backend/parser/scansup.c,v 1.39 2010/01/02 16:57:50 momjian Exp $
12+
* $PostgreSQL: pgsql/src/backend/parser/scansup.c,v 1.40 2010/05/08 16:39:49 tgl Exp $
1313
*
1414
*-------------------------------------------------------------------------
1515
*/
@@ -176,10 +176,20 @@ truncate_identifier(char *ident, int len, bool warn)
176176
{
177177
len = pg_mbcliplen(ident, len, NAMEDATALEN - 1);
178178
if (warn)
179+
{
180+
/*
181+
* Cannot use %.*s here because some machines interpret %s's
182+
* precision in characters, others in bytes.
183+
*/
184+
char buf[NAMEDATALEN];
185+
186+
memcpy(buf, ident, len);
187+
buf[len] = '\0';
179188
ereport(NOTICE,
180189
(errcode(ERRCODE_NAME_TOO_LONG),
181-
errmsg("identifier \"%s\" will be truncated to \"%.*s\"",
182-
ident, len, ident)));
190+
errmsg("identifier \"%s\" will be truncated to \"%s\"",
191+
ident, buf)));
192+
}
183193
ident[len] = '\0';
184194
}
185195
}

src/backend/tsearch/wparser_def.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*
88
*
99
* IDENTIFICATION
10-
* $PostgreSQL: pgsql/src/backend/tsearch/wparser_def.c,v 1.30 2010/04/28 02:04:16 tgl Exp $
10+
* $PostgreSQL: pgsql/src/backend/tsearch/wparser_def.c,v 1.31 2010/05/08 16:39:49 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -322,6 +322,12 @@ TParserInit(char *str, int len)
322322
prs->state->state = TPS_Base;
323323

324324
#ifdef WPARSER_TRACE
325+
/*
326+
* Use of %.*s here is not portable when the string contains multibyte
327+
* characters: some machines interpret the length in characters, others
328+
* in bytes. Since it's only a debugging aid, we haven't bothered to
329+
* fix this.
330+
*/
325331
fprintf(stderr, "parsing \"%.*s\"\n", len, str);
326332
#endif
327333

@@ -361,6 +367,7 @@ TParserCopyInit(const TParser *orig)
361367
prs->state->state = TPS_Base;
362368

363369
#ifdef WPARSER_TRACE
370+
/* See note above about %.*s */
364371
fprintf(stderr, "parsing copy of \"%.*s\"\n", prs->lenstr, prs->str);
365372
#endif
366373

src/backend/utils/adt/datetime.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.210 2010/01/02 16:57:53 momjian Exp $
11+
* $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.211 2010/05/08 16:39:51 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -3740,6 +3740,14 @@ EncodeDateTime(struct pg_tm * tm, fsec_t fsec, int *tzp, char **tzn, int style,
37403740

37413741
AppendTimestampSeconds(str + strlen(str), tm, fsec);
37423742

3743+
/*
3744+
* Note: the uses of %.*s in this function would be unportable
3745+
* if the timezone names ever contain non-ASCII characters,
3746+
* since some platforms think the string length is measured
3747+
* in characters not bytes. However, all TZ abbreviations in
3748+
* the Olson database are plain ASCII.
3749+
*/
3750+
37433751
if (tzp != NULL && tm->tm_isdst >= 0)
37443752
{
37453753
if (*tzn != NULL)
@@ -4091,6 +4099,7 @@ CheckDateTokenTable(const char *tablename, const datetkn *base, int nel)
40914099
{
40924100
if (strncmp(base[i - 1].token, base[i].token, TOKMAXLEN) >= 0)
40934101
{
4102+
/* %.*s is safe since all our tokens are ASCII */
40944103
elog(LOG, "ordering error in %s table: \"%.*s\" >= \"%.*s\"",
40954104
tablename,
40964105
TOKMAXLEN, base[i - 1].token,

src/backend/utils/error/elog.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
*
4343
*
4444
* IDENTIFICATION
45-
* $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.223 2010/02/26 02:01:12 momjian Exp $
45+
* $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.224 2010/05/08 16:39:51 tgl Exp $
4646
*
4747
*-------------------------------------------------------------------------
4848
*/
@@ -1871,7 +1871,7 @@ log_line_prefix(StringInfo buf, ErrorData *edata)
18711871
int displen;
18721872

18731873
psdisp = get_ps_display(&displen);
1874-
appendStringInfo(buf, "%.*s", displen, psdisp);
1874+
appendBinaryStringInfo(buf, psdisp, displen);
18751875
}
18761876
break;
18771877
case 'r':
@@ -2029,7 +2029,7 @@ write_csvlog(ErrorData *edata)
20292029
initStringInfo(&msgbuf);
20302030

20312031
psdisp = get_ps_display(&displen);
2032-
appendStringInfo(&msgbuf, "%.*s", displen, psdisp);
2032+
appendBinaryStringInfo(&msgbuf, psdisp, displen);
20332033
appendCSVLiteral(&buf, msgbuf.data);
20342034

20352035
pfree(msgbuf.data);

src/bin/psql/command.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*
44
* Copyright (c) 2000-2010, PostgreSQL Global Development Group
55
*
6-
* $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.218 2010/04/03 20:55:57 tgl Exp $
6+
* $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.219 2010/05/08 16:39:51 tgl Exp $
77
*/
88
#include "postgres_fe.h"
99
#include "command.h"
@@ -651,6 +651,13 @@ exec_command(const char *cmd,
651651
{
652652
char *opt = psql_scan_slash_option(scan_state,
653653
OT_WHOLE_LINE, NULL, false);
654+
size_t len;
655+
656+
/* strip any trailing spaces and semicolons */
657+
len = strlen(opt);
658+
while (len > 0 &&
659+
(isspace((unsigned char) opt[len - 1]) || opt[len - 1] == ';'))
660+
opt[--len] = '\0';
654661

655662
helpSQL(opt, pset.popt.topt.pager);
656663
free(opt);

src/bin/psql/help.c

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*
44
* Copyright (c) 2000-2010, PostgreSQL Global Development Group
55
*
6-
* $PostgreSQL: pgsql/src/bin/psql/help.c,v 1.157 2010/03/07 17:02:34 mha Exp $
6+
* $PostgreSQL: pgsql/src/bin/psql/help.c,v 1.158 2010/05/08 16:39:51 tgl Exp $
77
*/
88
#include "postgres_fe.h"
99

@@ -284,6 +284,7 @@ slashUsage(unsigned short int pager)
284284
/*
285285
* helpSQL -- help with SQL commands
286286
*
287+
* Note: we assume caller removed any trailing spaces in "topic".
287288
*/
288289
void
289290
helpSQL(const char *topic, unsigned short int pager)
@@ -352,17 +353,16 @@ helpSQL(const char *topic, unsigned short int pager)
352353
wordlen;
353354
int nl_count = 0;
354355

355-
/* User gets two chances: exact match, then the first word */
356-
357-
/* First pass : strip trailing spaces and semicolons */
356+
/*
357+
* We first try exact match, then first + second words, then first
358+
* word only.
359+
*/
358360
len = strlen(topic);
359-
while (topic[len - 1] == ' ' || topic[len - 1] == ';')
360-
len--;
361361

362-
for (x = 1; x <= 3; x++) /* Three chances to guess that word... */
362+
for (x = 1; x <= 3; x++)
363363
{
364364
if (x > 1) /* Nothing on first pass - try the opening
365-
* words */
365+
* word(s) */
366366
{
367367
wordlen = j = 1;
368368
while (topic[j] != ' ' && j++ < len)
@@ -423,7 +423,7 @@ helpSQL(const char *topic, unsigned short int pager)
423423
}
424424

425425
if (!help_found)
426-
fprintf(output, _("No help available for \"%-.*s\".\nTry \\h with no arguments to see available help.\n"), (int) len, topic);
426+
fprintf(output, _("No help available for \"%s\".\nTry \\h with no arguments to see available help.\n"), topic);
427427

428428
/* Only close if we used the pager */
429429
if (output != stdout)

src/bin/psql/print.c

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*
44
* Copyright (c) 2000-2010, PostgreSQL Global Development Group
55
*
6-
* $PostgreSQL: pgsql/src/bin/psql/print.c,v 1.124 2010/03/01 21:27:26 heikki Exp $
6+
* $PostgreSQL: pgsql/src/bin/psql/print.c,v 1.125 2010/05/08 16:39:52 tgl Exp $
77
*/
88
#include "postgres_fe.h"
99

@@ -252,6 +252,20 @@ format_numeric_locale(const char *my_str)
252252
}
253253

254254

255+
/*
256+
* fputnbytes: print exactly N bytes to a file
257+
*
258+
* Think not to use fprintf with a %.*s format for this. Some machines
259+
* believe %s's precision is measured in characters, others in bytes.
260+
*/
261+
static void
262+
fputnbytes(FILE *f, const char *str, size_t n)
263+
{
264+
while (n-- > 0)
265+
fputc(*str++, f);
266+
}
267+
268+
255269
/*************************/
256270
/* Unaligned text */
257271
/*************************/
@@ -913,14 +927,16 @@ print_aligned_text(const printTableContent *cont, FILE *fout)
913927
{
914928
/* spaces first */
915929
fprintf(fout, "%*s", width_wrap[j] - chars_to_output, "");
916-
fprintf(fout, "%.*s", bytes_to_output,
917-
this_line->ptr + bytes_output[j]);
930+
fputnbytes(fout,
931+
this_line->ptr + bytes_output[j],
932+
bytes_to_output);
918933
}
919934
else /* Left aligned cell */
920935
{
921936
/* spaces second */
922-
fprintf(fout, "%.*s", bytes_to_output,
923-
this_line->ptr + bytes_output[j]);
937+
fputnbytes(fout,
938+
this_line->ptr + bytes_output[j],
939+
bytes_to_output);
924940
}
925941

926942
bytes_output[j] += bytes_to_output;

src/interfaces/ecpg/ecpglib/error.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* $PostgreSQL: pgsql/src/interfaces/ecpg/ecpglib/error.c,v 1.25 2010/03/08 12:15:24 meskes Exp $ */
1+
/* $PostgreSQL: pgsql/src/interfaces/ecpg/ecpglib/error.c,v 1.26 2010/05/08 16:39:52 tgl Exp $ */
22

33
#define POSTGRES_ECPG_INTERNAL
44
#include "postgres_fe.h"
@@ -332,6 +332,7 @@ ecpg_raise_backend(int line, PGresult *result, PGconn *conn, int compat)
332332
else
333333
sqlca->sqlcode = ECPG_PGSQL;
334334

335+
/* %.*s is safe here as long as sqlstate is all-ASCII */
335336
ecpg_log("raising sqlstate %.*s (sqlcode %d): %s\n",
336337
sizeof(sqlca->sqlstate), sqlca->sqlstate, sqlca->sqlcode, sqlca->sqlerrm.sqlerrmc);
337338

src/interfaces/ecpg/pgtypeslib/dt_common.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* $PostgreSQL: pgsql/src/interfaces/ecpg/pgtypeslib/dt_common.c,v 1.51 2009/06/11 14:49:13 momjian Exp $ */
1+
/* $PostgreSQL: pgsql/src/interfaces/ecpg/pgtypeslib/dt_common.c,v 1.52 2010/05/08 16:39:52 tgl Exp $ */
22

33
#include "postgres_fe.h"
44

@@ -855,6 +855,14 @@ EncodeDateTime(struct tm * tm, fsec_t fsec, int *tzp, char **tzn, int style, cha
855855
if (tm->tm_year <= 0)
856856
sprintf(str + strlen(str), " BC");
857857

858+
/*
859+
* Note: the uses of %.*s in this function would be unportable
860+
* if the timezone names ever contain non-ASCII characters,
861+
* since some platforms think the string length is measured
862+
* in characters not bytes. However, all TZ abbreviations in
863+
* the Olson database are plain ASCII.
864+
*/
865+
858866
if (tzp != NULL && tm->tm_isdst >= 0)
859867
{
860868
if (*tzn != NULL)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy