diff options
-rw-r--r-- | meta-oe/recipes-support/postgresql/files/0008-Prevent-potential-overruns-of-fixed-size-buffers.patch | 393 | ||||
-rw-r--r-- | meta-oe/recipes-support/postgresql/postgresql.inc | 1 |
2 files changed, 394 insertions, 0 deletions
diff --git a/meta-oe/recipes-support/postgresql/files/0008-Prevent-potential-overruns-of-fixed-size-buffers.patch b/meta-oe/recipes-support/postgresql/files/0008-Prevent-potential-overruns-of-fixed-size-buffers.patch new file mode 100644 index 0000000000..62ec9354d4 --- /dev/null +++ b/meta-oe/recipes-support/postgresql/files/0008-Prevent-potential-overruns-of-fixed-size-buffers.patch @@ -0,0 +1,393 @@ +From 655b665f745e2e07cf6936c6063b0250f5caa98f Mon Sep 17 00:00:00 2001 +From: Tom Lane <tgl@sss.pgh.pa.us> +Date: Mon, 17 Feb 2014 11:20:27 -0500 +Subject: [PATCH] Prevent potential overruns of fixed-size buffers. + +commit 655b665f745e2e07cf6936c6063b0250f5caa98f REL9_2_STABLE + +Coverity identified a number of places in which it couldn't prove that a +string being copied into a fixed-size buffer would fit. We believe that +most, perhaps all of these are in fact safe, or are copying data that is +coming from a trusted source so that any overrun is not really a security +issue. Nonetheless it seems prudent to forestall any risk by using +strlcpy() and similar functions. + +Fixes by Peter Eisentraut and Jozef Mlich based on Coverity reports. + +In addition, fix a potential null-pointer-dereference crash in +contrib/chkpass. The crypt(3) function is defined to return NULL on +failure, but chkpass.c didn't check for that before using the result. +The main practical case in which this could be an issue is if libc is +configured to refuse to execute unapproved hashing algorithms (e.g., +"FIPS mode"). This ideally should've been a separate commit, but +since it touches code adjacent to one of the buffer overrun changes, +I included it in this commit to avoid last-minute merge issues. +This issue was reported by Honza Horak. + +Security: CVE-2014-0065 for buffer overruns, CVE-2014-0066 for crypt() + +Upsteam-Status: Backport + +Signed-off-by: Kai Kang <kai.kang@windriver.com> +--- + contrib/chkpass/chkpass.c | 29 ++++++++++++++++++++++++++--- + contrib/pg_standby/pg_standby.c | 2 +- + src/backend/access/transam/xlog.c | 10 +++++----- + src/backend/tsearch/spell.c | 2 +- + src/backend/utils/adt/datetime.c | 11 ++++++----- + src/bin/initdb/findtimezone.c | 4 ++-- + src/bin/pg_basebackup/pg_basebackup.c | 8 ++++---- + src/interfaces/ecpg/preproc/pgc.l | 2 +- + src/interfaces/libpq/fe-protocol2.c | 2 +- + src/interfaces/libpq/fe-protocol3.c | 2 +- + src/port/exec.c | 4 ++-- + src/test/regress/pg_regress.c | 6 +++--- + src/timezone/pgtz.c | 2 +- + 13 files changed, 54 insertions(+), 30 deletions(-) + +diff --git a/contrib/chkpass/chkpass.c b/contrib/chkpass/chkpass.c +index 0c9fec0..1795b8c 100644 +--- a/contrib/chkpass/chkpass.c ++++ b/contrib/chkpass/chkpass.c +@@ -70,6 +70,7 @@ chkpass_in(PG_FUNCTION_ARGS) + char *str = PG_GETARG_CSTRING(0); + chkpass *result; + char mysalt[4]; ++ char *crypt_output; + static char salt_chars[] = + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +@@ -92,7 +93,15 @@ chkpass_in(PG_FUNCTION_ARGS) + mysalt[1] = salt_chars[random() & 0x3f]; + mysalt[2] = 0; /* technically the terminator is not necessary + * but I like to play safe */ +- strcpy(result->password, crypt(str, mysalt)); ++ ++ crypt_output = crypt(str, mysalt); ++ if (crypt_output == NULL) ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_PARAMETER_VALUE), ++ errmsg("crypt() failed"))); ++ ++ strlcpy(result->password, crypt_output, sizeof(result->password)); ++ + PG_RETURN_POINTER(result); + } + +@@ -141,9 +150,16 @@ chkpass_eq(PG_FUNCTION_ARGS) + chkpass *a1 = (chkpass *) PG_GETARG_POINTER(0); + text *a2 = PG_GETARG_TEXT_PP(1); + char str[9]; ++ char *crypt_output; + + text_to_cstring_buffer(a2, str, sizeof(str)); +- PG_RETURN_BOOL(strcmp(a1->password, crypt(str, a1->password)) == 0); ++ crypt_output = crypt(str, a1->password); ++ if (crypt_output == NULL) ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_PARAMETER_VALUE), ++ errmsg("crypt() failed"))); ++ ++ PG_RETURN_BOOL(strcmp(a1->password, crypt_output) == 0); + } + + PG_FUNCTION_INFO_V1(chkpass_ne); +@@ -153,7 +169,14 @@ chkpass_ne(PG_FUNCTION_ARGS) + chkpass *a1 = (chkpass *) PG_GETARG_POINTER(0); + text *a2 = PG_GETARG_TEXT_PP(1); + char str[9]; ++ char *crypt_output; + + text_to_cstring_buffer(a2, str, sizeof(str)); +- PG_RETURN_BOOL(strcmp(a1->password, crypt(str, a1->password)) != 0); ++ crypt_output = crypt(str, a1->password); ++ if (crypt_output == NULL) ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_PARAMETER_VALUE), ++ errmsg("crypt() failed"))); ++ ++ PG_RETURN_BOOL(strcmp(a1->password, crypt_output) != 0); + } +diff --git a/contrib/pg_standby/pg_standby.c b/contrib/pg_standby/pg_standby.c +index 84941ed..0f1e0c1 100644 +--- a/contrib/pg_standby/pg_standby.c ++++ b/contrib/pg_standby/pg_standby.c +@@ -338,7 +338,7 @@ SetWALFileNameForCleanup(void) + if (strcmp(restartWALFileName, nextWALFileName) > 0) + return false; + +- strcpy(exclusiveCleanupFileName, restartWALFileName); ++ strlcpy(exclusiveCleanupFileName, restartWALFileName, sizeof(exclusiveCleanupFileName)); + return true; + } + +diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c +index d639c4a..49bb453 100644 +--- a/src/backend/access/transam/xlog.c ++++ b/src/backend/access/transam/xlog.c +@@ -3017,7 +3017,7 @@ KeepFileRestoredFromArchive(char *path, char *xlogfname) + xlogfpath, oldpath))); + } + #else +- strncpy(oldpath, xlogfpath, MAXPGPATH); ++ strlcpy(oldpath, xlogfpath, MAXPGPATH); + #endif + if (unlink(oldpath) != 0) + ereport(FATAL, +@@ -5913,7 +5913,7 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis) + + recordRestorePointData = (xl_restore_point *) XLogRecGetData(record); + recordXtime = recordRestorePointData->rp_time; +- strncpy(recordRPName, recordRestorePointData->rp_name, MAXFNAMELEN); ++ strlcpy(recordRPName, recordRestorePointData->rp_name, MAXFNAMELEN); + } + else + return false; +@@ -6008,7 +6008,7 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis) + } + else + { +- strncpy(recoveryStopName, recordRPName, MAXFNAMELEN); ++ strlcpy(recoveryStopName, recordRPName, MAXFNAMELEN); + + ereport(LOG, + (errmsg("recovery stopping at restore point \"%s\", time %s", +@@ -6348,7 +6348,7 @@ StartupXLOG(void) + * see them + */ + XLogCtl->RecoveryTargetTLI = recoveryTargetTLI; +- strncpy(XLogCtl->archiveCleanupCommand, ++ strlcpy(XLogCtl->archiveCleanupCommand, + archiveCleanupCommand ? archiveCleanupCommand : "", + sizeof(XLogCtl->archiveCleanupCommand)); + +@@ -8760,7 +8760,7 @@ XLogRestorePoint(const char *rpName) + xl_restore_point xlrec; + + xlrec.rp_time = GetCurrentTimestamp(); +- strncpy(xlrec.rp_name, rpName, MAXFNAMELEN); ++ strlcpy(xlrec.rp_name, rpName, MAXFNAMELEN); + + rdata.buffer = InvalidBuffer; + rdata.data = (char *) &xlrec; +diff --git a/src/backend/tsearch/spell.c b/src/backend/tsearch/spell.c +index 449aa6a..4acc33e 100644 +--- a/src/backend/tsearch/spell.c ++++ b/src/backend/tsearch/spell.c +@@ -255,7 +255,7 @@ NIAddSpell(IspellDict *Conf, const char *word, const char *flag) + } + Conf->Spell[Conf->nspell] = (SPELL *) tmpalloc(SPELLHDRSZ + strlen(word) + 1); + strcpy(Conf->Spell[Conf->nspell]->word, word); +- strncpy(Conf->Spell[Conf->nspell]->p.flag, flag, MAXFLAGLEN); ++ strlcpy(Conf->Spell[Conf->nspell]->p.flag, flag, MAXFLAGLEN); + Conf->nspell++; + } + +diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c +index 4763a6f..4105f17 100644 +--- a/src/backend/utils/adt/datetime.c ++++ b/src/backend/utils/adt/datetime.c +@@ -90,10 +90,10 @@ char *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday", + * Note that this table must be strictly alphabetically ordered to allow an + * O(ln(N)) search algorithm to be used. + * +- * The text field is NOT guaranteed to be NULL-terminated. ++ * The token field is NOT guaranteed to be NULL-terminated. + * +- * To keep this table reasonably small, we divide the lexval for TZ and DTZ +- * entries by 15 (so they are on 15 minute boundaries) and truncate the text ++ * To keep this table reasonably small, we divide the value for TZ and DTZ ++ * entries by 15 (so they are on 15 minute boundaries) and truncate the token + * field at TOKMAXLEN characters. + * Formerly, we divided by 10 rather than 15 but there are a few time zones + * which are 30 or 45 minutes away from an even hour, most are on an hour +@@ -108,7 +108,7 @@ static datetkn *timezonetktbl = NULL; + static int sztimezonetktbl = 0; + + static const datetkn datetktbl[] = { +-/* text, token, lexval */ ++ /* token, type, value */ + {EARLY, RESERV, DTK_EARLY}, /* "-infinity" reserved for "early time" */ + {DA_D, ADBC, AD}, /* "ad" for years > 0 */ + {"allballs", RESERV, DTK_ZULU}, /* 00:00:00 */ +@@ -188,7 +188,7 @@ static const datetkn datetktbl[] = { + static int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0]; + + static datetkn deltatktbl[] = { +- /* text, token, lexval */ ++ /* token, type, value */ + {"@", IGNORE_DTF, 0}, /* postgres relative prefix */ + {DAGO, AGO, 0}, /* "ago" indicates negative time offset */ + {"c", UNITS, DTK_CENTURY}, /* "century" relative */ +@@ -4201,6 +4201,7 @@ ConvertTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl, + tbl->numabbrevs = n; + for (i = 0; i < n; i++) + { ++ /* do NOT use strlcpy here; token field need not be null-terminated */ + strncpy(newtbl[i].token, abbrevs[i].abbrev, TOKMAXLEN); + newtbl[i].type = abbrevs[i].is_dst ? DTZ : TZ; + TOVAL(&newtbl[i], abbrevs[i].offset / MINS_PER_HOUR); +diff --git a/src/bin/initdb/findtimezone.c b/src/bin/initdb/findtimezone.c +index 6d6f96a..6d38151 100644 +--- a/src/bin/initdb/findtimezone.c ++++ b/src/bin/initdb/findtimezone.c +@@ -68,7 +68,7 @@ pg_open_tzfile(const char *name, char *canonname) + if (canonname) + strlcpy(canonname, name, TZ_STRLEN_MAX + 1); + +- strcpy(fullname, pg_TZDIR()); ++ strlcpy(fullname, pg_TZDIR(), sizeof(fullname)); + if (strlen(fullname) + 1 + strlen(name) >= MAXPGPATH) + return -1; /* not gonna fit */ + strcat(fullname, "/"); +@@ -375,7 +375,7 @@ identify_system_timezone(void) + } + + /* Search for the best-matching timezone file */ +- strcpy(tmptzdir, pg_TZDIR()); ++ strlcpy(tmptzdir, pg_TZDIR(), sizeof(tmptzdir)); + bestscore = -1; + resultbuf[0] = '\0'; + scan_available_timezones(tmptzdir, tmptzdir + strlen(tmptzdir) + 1, +diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/pg_basebackup.c +index 9d840a1..26cc758 100644 +--- a/src/bin/pg_basebackup/pg_basebackup.c ++++ b/src/bin/pg_basebackup/pg_basebackup.c +@@ -735,9 +735,9 @@ ReceiveAndUnpackTarFile(PGconn *conn, PGresult *res, int rownum) + FILE *file = NULL; + + if (PQgetisnull(res, rownum, 0)) +- strcpy(current_path, basedir); ++ strlcpy(current_path, basedir, sizeof(current_path)); + else +- strcpy(current_path, PQgetvalue(res, rownum, 1)); ++ strlcpy(current_path, PQgetvalue(res, rownum, 1), sizeof(current_path)); + + /* + * Get the COPY data +@@ -1053,7 +1053,7 @@ BaseBackup(void) + progname); + disconnect_and_exit(1); + } +- strcpy(xlogstart, PQgetvalue(res, 0, 0)); ++ strlcpy(xlogstart, PQgetvalue(res, 0, 0), sizeof(xlogstart)); + if (verbose && includewal) + fprintf(stderr, "transaction log start point: %s\n", xlogstart); + PQclear(res); +@@ -1153,7 +1153,7 @@ BaseBackup(void) + progname); + disconnect_and_exit(1); + } +- strcpy(xlogend, PQgetvalue(res, 0, 0)); ++ strlcpy(xlogend, PQgetvalue(res, 0, 0), sizeof(xlogend)); + if (verbose && includewal) + fprintf(stderr, "transaction log end point: %s\n", xlogend); + PQclear(res); +diff --git a/src/interfaces/ecpg/preproc/pgc.l b/src/interfaces/ecpg/preproc/pgc.l +index f2e7edd..7ae8556 100644 +--- a/src/interfaces/ecpg/preproc/pgc.l ++++ b/src/interfaces/ecpg/preproc/pgc.l +@@ -1315,7 +1315,7 @@ parse_include(void) + yytext[i] = '\0'; + memmove(yytext, yytext+1, strlen(yytext)); + +- strncpy(inc_file, yytext, sizeof(inc_file)); ++ strlcpy(inc_file, yytext, sizeof(inc_file)); + yyin = fopen(inc_file, "r"); + if (!yyin) + { +diff --git a/src/interfaces/libpq/fe-protocol2.c b/src/interfaces/libpq/fe-protocol2.c +index 1ba5885..af4c412 100644 +--- a/src/interfaces/libpq/fe-protocol2.c ++++ b/src/interfaces/libpq/fe-protocol2.c +@@ -500,7 +500,7 @@ pqParseInput2(PGconn *conn) + if (!conn->result) + return; + } +- strncpy(conn->result->cmdStatus, conn->workBuffer.data, ++ strlcpy(conn->result->cmdStatus, conn->workBuffer.data, + CMDSTATUS_LEN); + checkXactStatus(conn, conn->workBuffer.data); + conn->asyncStatus = PGASYNC_READY; +diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c +index d289f82..6f8a470 100644 +--- a/src/interfaces/libpq/fe-protocol3.c ++++ b/src/interfaces/libpq/fe-protocol3.c +@@ -206,7 +206,7 @@ pqParseInput3(PGconn *conn) + if (!conn->result) + return; + } +- strncpy(conn->result->cmdStatus, conn->workBuffer.data, ++ strlcpy(conn->result->cmdStatus, conn->workBuffer.data, + CMDSTATUS_LEN); + conn->asyncStatus = PGASYNC_READY; + break; +diff --git a/src/port/exec.c b/src/port/exec.c +index c79e8ba..0726dbe 100644 +--- a/src/port/exec.c ++++ b/src/port/exec.c +@@ -66,7 +66,7 @@ validate_exec(const char *path) + if (strlen(path) >= strlen(".exe") && + pg_strcasecmp(path + strlen(path) - strlen(".exe"), ".exe") != 0) + { +- strcpy(path_exe, path); ++ strlcpy(path_exe, path, sizeof(path_exe) - 4); + strcat(path_exe, ".exe"); + path = path_exe; + } +@@ -275,7 +275,7 @@ resolve_symlinks(char *path) + } + + /* must copy final component out of 'path' temporarily */ +- strcpy(link_buf, fname); ++ strlcpy(link_buf, fname, sizeof(link_buf)); + + if (!getcwd(path, MAXPGPATH)) + { +diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c +index d991a5c..a6466eb 100644 +--- a/src/test/regress/pg_regress.c ++++ b/src/test/regress/pg_regress.c +@@ -1233,7 +1233,7 @@ results_differ(const char *testname, const char *resultsfile, const char *defaul + */ + platform_expectfile = get_expectfile(testname, resultsfile); + +- strcpy(expectfile, default_expectfile); ++ strlcpy(expectfile, default_expectfile, sizeof(expectfile)); + if (platform_expectfile) + { + /* +@@ -1288,7 +1288,7 @@ results_differ(const char *testname, const char *resultsfile, const char *defaul + { + /* This diff was a better match than the last one */ + best_line_count = l; +- strcpy(best_expect_file, alt_expectfile); ++ strlcpy(best_expect_file, alt_expectfile, sizeof(best_expect_file)); + } + free(alt_expectfile); + } +@@ -1316,7 +1316,7 @@ results_differ(const char *testname, const char *resultsfile, const char *defaul + { + /* This diff was a better match than the last one */ + best_line_count = l; +- strcpy(best_expect_file, default_expectfile); ++ strlcpy(best_expect_file, default_expectfile, sizeof(best_expect_file)); + } + } + +diff --git a/src/timezone/pgtz.c b/src/timezone/pgtz.c +index d5bc83e..80c5635 100644 +--- a/src/timezone/pgtz.c ++++ b/src/timezone/pgtz.c +@@ -83,7 +83,7 @@ pg_open_tzfile(const char *name, char *canonname) + * Loop to split the given name into directory levels; for each level, + * search using scan_directory_ci(). + */ +- strcpy(fullname, pg_TZDIR()); ++ strlcpy(fullname, pg_TZDIR(), sizeof(fullname)); + orignamelen = fullnamelen = strlen(fullname); + fname = name; + for (;;) +-- +1.7.5.4 + diff --git a/meta-oe/recipes-support/postgresql/postgresql.inc b/meta-oe/recipes-support/postgresql/postgresql.inc index ce31205079..1397f564de 100644 --- a/meta-oe/recipes-support/postgresql/postgresql.inc +++ b/meta-oe/recipes-support/postgresql/postgresql.inc @@ -37,6 +37,7 @@ SRC_URI = "http://ftp.postgresql.org/pub/source/v${PV}/${BP}.tar.bz2 \ file://0005-Avoid-repeated-name-lookups-during-table-and-index-D.patch \ file://0006-Fix-handling-of-wide-datetime-input-output.patch \ file://0007-Make-pqsignal-available-to-pg_regress-of-ECPG-and-is.patch \ + file://0008-Prevent-potential-overruns-of-fixed-size-buffers.patch \ " LEAD_SONAME = "libpq.so" |