From 65da0d66b4e89951078ebc43a5343780e4e700d6 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Fri, 7 Jul 2000 21:12:53 +0000 Subject: [PATCH] Fix misuse of StrNCpy to copy and add null to non-null-terminated data. Does not work since it fetches one byte beyond the source data, and when the phase of the moon is wrong, the source data is smack up against the end of backend memory and you get SIGSEGV. Don't laugh, this is a fix for an actual user bug report. --- contrib/miscutil/misc_utils.c | 4 ++-- src/backend/libpq/be-fsstubs.c | 26 +++++++++++--------------- src/backend/libpq/be-pqexec.c | 6 +++--- src/backend/port/dynloader/aix.c | 3 ++- src/backend/utils/adt/like.c | 5 +++-- src/backend/utils/adt/not_in.c | 12 +++++++----- src/backend/utils/adt/regexp.c | 5 +++-- src/backend/utils/adt/varchar.c | 18 +++++++++++------- src/include/c.h | 20 ++++++++++++++------ 9 files changed, 56 insertions(+), 43 deletions(-) diff --git a/contrib/miscutil/misc_utils.c b/contrib/miscutil/misc_utils.c index f5a9868328..e7949d32aa 100644 --- a/contrib/miscutil/misc_utils.c +++ b/contrib/miscutil/misc_utils.c @@ -99,9 +99,9 @@ active_listeners(text *relname) if (relname && (VARSIZE(relname) > VARHDRSZ)) { + MemSet(listen_name, 0, NAMEDATALEN); len = MIN(VARSIZE(relname) - VARHDRSZ, NAMEDATALEN - 1); - strncpy(listen_name, VARDATA(relname), len); - listen_name[len] = '\0'; + memcpy(listen_name, VARDATA(relname), len); ScanKeyEntryInitialize(&key, 0, Anum_pg_listener_relname, F_NAMEEQ, diff --git a/src/backend/libpq/be-fsstubs.c b/src/backend/libpq/be-fsstubs.c index 91769120f9..929ddad5aa 100644 --- a/src/backend/libpq/be-fsstubs.c +++ b/src/backend/libpq/be-fsstubs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/libpq/be-fsstubs.c,v 1.48 2000/07/03 23:09:37 wieck Exp $ + * $Header: /cvsroot/pgsql/src/backend/libpq/be-fsstubs.c,v 1.49 2000/07/07 21:12:53 tgl Exp $ * * NOTES * This should be moved to a more appropriate place. It is here @@ -379,26 +379,23 @@ lo_import(PG_FUNCTION_ARGS) /* * open the file to be read in */ - nbytes = VARSIZE(filename) - VARHDRSZ + 1; - if (nbytes > FNAME_BUFSIZE) - nbytes = FNAME_BUFSIZE; - StrNCpy(fnamebuf, VARDATA(filename), nbytes); + nbytes = VARSIZE(filename) - VARHDRSZ; + if (nbytes >= FNAME_BUFSIZE) + nbytes = FNAME_BUFSIZE-1; + memcpy(fnamebuf, VARDATA(filename), nbytes); + fnamebuf[nbytes] = '\0'; fd = PathNameOpenFile(fnamebuf, O_RDONLY | PG_BINARY, 0666); if (fd < 0) - { /* error */ elog(ERROR, "lo_import: can't open unix file \"%s\": %m", fnamebuf); - } /* * create an inversion "object" */ lobj = inv_create(INV_READ | INV_WRITE); if (lobj == NULL) - { elog(ERROR, "lo_import: can't create inv object for \"%s\"", fnamebuf); - } /* * the oid for the large object is just the oid of the relation @@ -461,18 +458,17 @@ lo_export(PG_FUNCTION_ARGS) * 022. This code used to drop it all the way to 0, but creating * world-writable export files doesn't seem wise. */ - nbytes = VARSIZE(filename) - VARHDRSZ + 1; - if (nbytes > FNAME_BUFSIZE) - nbytes = FNAME_BUFSIZE; - StrNCpy(fnamebuf, VARDATA(filename), nbytes); + nbytes = VARSIZE(filename) - VARHDRSZ; + if (nbytes >= FNAME_BUFSIZE) + nbytes = FNAME_BUFSIZE-1; + memcpy(fnamebuf, VARDATA(filename), nbytes); + fnamebuf[nbytes] = '\0'; oumask = umask((mode_t) 0022); fd = PathNameOpenFile(fnamebuf, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY, 0666); umask(oumask); if (fd < 0) - { /* error */ elog(ERROR, "lo_export: can't open unix file \"%s\": %m", fnamebuf); - } /* * read in from the Unix file and write to the inversion file diff --git a/src/backend/libpq/be-pqexec.c b/src/backend/libpq/be-pqexec.c index 78745d50a7..474da58779 100644 --- a/src/backend/libpq/be-pqexec.c +++ b/src/backend/libpq/be-pqexec.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/libpq/Attic/be-pqexec.c,v 1.35 2000/07/05 23:11:19 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/libpq/Attic/be-pqexec.c,v 1.36 2000/07/07 21:12:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -236,8 +236,8 @@ strmake(char *str, int len) if (len <= 0) len = strlen(str); - newstr = (char *) palloc((unsigned) len + 1); - StrNCpy(newstr, str, len + 1); + newstr = (char *) palloc(len + 1); + memcpy(newstr, str, len); newstr[len] = (char) 0; return newstr; } diff --git a/src/backend/port/dynloader/aix.c b/src/backend/port/dynloader/aix.c index 6170555143..c6295406e2 100644 --- a/src/backend/port/dynloader/aix.c +++ b/src/backend/port/dynloader/aix.c @@ -536,7 +536,8 @@ readExports(ModulePtr mp) * first SYMNMLEN chars and make sure we have a zero byte at * the end. */ - StrNCpy(tmpsym, ls->l_name, SYMNMLEN + 1); + strncpy(tmpsym, ls->l_name, SYMNMLEN); + tmpsym[SYMNMLEN] = '\0'; symname = tmpsym; } ep->name = strdup(symname); diff --git a/src/backend/utils/adt/like.c b/src/backend/utils/adt/like.c index 6fbd95ffe2..5a7b847339 100644 --- a/src/backend/utils/adt/like.c +++ b/src/backend/utils/adt/like.c @@ -11,7 +11,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/like.c,v 1.36 2000/07/06 05:48:11 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/like.c,v 1.37 2000/07/07 21:12:50 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -48,7 +48,8 @@ fixedlen_like(char *s, text *p, int charlen) (void) pg_mb2wchar_with_len((unsigned char *) s, sterm, charlen); #else sterm = (char *) palloc(charlen + 1); - StrNCpy(sterm, s, charlen + 1); + memcpy(sterm, s, charlen); + sterm[charlen] = '\0'; #endif /* diff --git a/src/backend/utils/adt/not_in.c b/src/backend/utils/adt/not_in.c index ec3b82c502..55182f1bf9 100644 --- a/src/backend/utils/adt/not_in.c +++ b/src/backend/utils/adt/not_in.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/not_in.c,v 1.23 2000/06/09 01:11:09 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/not_in.c,v 1.24 2000/07/07 21:12:50 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -52,10 +52,12 @@ int4notin(PG_FUNCTION_ARGS) char my_copy[NAMEDATALEN * 2 + 2]; Datum value; - strlength = VARSIZE(relation_and_attr) - VARHDRSZ + 1; - if (strlength > sizeof(my_copy)) - strlength = sizeof(my_copy); - StrNCpy(my_copy, VARDATA(relation_and_attr), strlength); + /* make a null-terminated copy of text */ + strlength = VARSIZE(relation_and_attr) - VARHDRSZ; + if (strlength >= sizeof(my_copy)) + strlength = sizeof(my_copy)-1; + memcpy(my_copy, VARDATA(relation_and_attr), strlength); + my_copy[strlength] = '\0'; relation = (char *) strtok(my_copy, "."); attribute = (char *) strtok(NULL, "."); diff --git a/src/backend/utils/adt/regexp.c b/src/backend/utils/adt/regexp.c index dcf158c7a2..5e9a91dbf4 100644 --- a/src/backend/utils/adt/regexp.c +++ b/src/backend/utils/adt/regexp.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/regexp.c,v 1.32 2000/07/06 05:48:11 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/regexp.c,v 1.33 2000/07/07 21:12:50 tgl Exp $ * * Alistair Crooks added the code for the regex caching * agc - cached the regular expressions used - there's a good chance @@ -164,7 +164,8 @@ fixedlen_regexeq(char *s, text *p, int charlen, int cflags) /* be sure sterm is null-terminated */ sterm = (char *) palloc(charlen + 1); - StrNCpy(sterm, s, charlen + 1); + memcpy(sterm, s, charlen); + sterm[charlen] = '\0'; result = RE_compile_and_execute(p, sterm, cflags); diff --git a/src/backend/utils/adt/varchar.c b/src/backend/utils/adt/varchar.c index 6f17cb589a..58b24f339e 100644 --- a/src/backend/utils/adt/varchar.c +++ b/src/backend/utils/adt/varchar.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/varchar.c,v 1.67 2000/07/03 23:09:53 wieck Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/varchar.c,v 1.68 2000/07/07 21:12:50 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -115,9 +115,11 @@ bpcharout(PG_FUNCTION_ARGS) char *result; int len; + /* copy and add null term */ len = VARSIZE(s) - VARHDRSZ; result = (char *) palloc(len + 1); - StrNCpy(result, VARDATA(s), len + 1); /* copy and add null term */ + memcpy(result, VARDATA(s), len); + result[len] = '\0'; #ifdef CYR_RECODE convertstr(result, len, 1); @@ -268,8 +270,8 @@ bpchar_name(char *s) return NULL; len = VARSIZE(s) - VARHDRSZ; - if (len > NAMEDATALEN) - len = NAMEDATALEN; + if (len >= NAMEDATALEN) + len = NAMEDATALEN-1; while (len > 0) { @@ -284,7 +286,7 @@ bpchar_name(char *s) #endif result = (NameData *) palloc(NAMEDATALEN); - StrNCpy(NameStr(*result), VARDATA(s), NAMEDATALEN); + memcpy(NameStr(*result), VARDATA(s), len); /* now null pad to full length... */ while (len < NAMEDATALEN) @@ -316,7 +318,7 @@ name_bpchar(NameData *s) #endif result = (char *) palloc(VARHDRSZ + len); - strncpy(VARDATA(result), NameStr(*s), len); + memcpy(VARDATA(result), NameStr(*s), len); VARATT_SIZEP(result) = len + VARHDRSZ; return result; @@ -365,9 +367,11 @@ varcharout(PG_FUNCTION_ARGS) char *result; int len; + /* copy and add null term */ len = VARSIZE(s) - VARHDRSZ; result = (char *) palloc(len + 1); - StrNCpy(result, VARDATA(s), len + 1); /* copy and add null term */ + memcpy(result, VARDATA(s), len); + result[len] = '\0'; #ifdef CYR_RECODE convertstr(result, len, 1); diff --git a/src/include/c.h b/src/include/c.h index 65ff45aa92..250a8c6db1 100644 --- a/src/include/c.h +++ b/src/include/c.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: c.h,v 1.75 2000/07/06 21:33:44 petere Exp $ + * $Id: c.h,v 1.76 2000/07/07 21:12:47 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -781,11 +781,19 @@ extern int assertTest(int val); /* * StrNCpy - * Like standard library function strncpy(), except that result string - * is guaranteed to be null-terminated --- that is, at most N-1 bytes - * of the source string will be kept. - * Also, the macro returns no result (too hard to do that without - * evaluating the arguments multiple times, which seems worse). + * Like standard library function strncpy(), except that result string + * is guaranteed to be null-terminated --- that is, at most N-1 bytes + * of the source string will be kept. + * Also, the macro returns no result (too hard to do that without + * evaluating the arguments multiple times, which seems worse). + * + * BTW: when you need to copy a non-null-terminated string (like a text + * datum) and add a null, do not do it with StrNCpy(..., len+1). That + * might seem to work, but it fetches one byte more than there is in the + * text object. One fine day you'll have a SIGSEGV because there isn't + * another byte before the end of memory. Don't laugh, we've had real + * live bug reports from real live users over exactly this mistake. + * Do it honestly with "memcpy(dst,src,len); dst[len] = '\0';", instead. */ #define StrNCpy(dst,src,len) \ do \