In the hopes of getting the cfbot un-stuck (it's currently trying to test a known-not-to-work patch), here are updated versions of the two live patches we have in this thread.
0001 is the patch I originally proposed to adjust printf archetypes. 0002 is Thomas's patch to blow up on errno references in ereport/elog, plus changes in src/common/exec.c to prevent that from blowing up. (I went with the minimum-footprint way, for now; making exec.c's error handling generally nicer seems like a task for another day.) I think 0002 is probably pushable, really. The unresolved issue about 0001 is whether it will create a spate of warnings on Windows builds, and if so what to do about it. We might learn something from the cfbot about that, but I think the full buildfarm is going to be the only really authoritative answer. There's also the matter of providing similar safety for GetLastError calls, but I think that deserves to be a separate patch ... and I don't really want to take point on it since I lack a Windows machine. regards, tom lane
diff --git a/config/c-compiler.m4 b/config/c-compiler.m4 index 9731a51..5e56ca5 100644 *** a/config/c-compiler.m4 --- b/config/c-compiler.m4 *************** fi])# PGAC_C_SIGNED *** 19,28 **** # PGAC_C_PRINTF_ARCHETYPE # ----------------------- ! # Set the format archetype used by gcc to check printf type functions. We ! # prefer "gnu_printf", which includes what glibc uses, such as %m for error ! # strings and %lld for 64 bit long longs. GCC 4.4 introduced it. It makes a ! # dramatic difference on Windows. AC_DEFUN([PGAC_PRINTF_ARCHETYPE], [AC_CACHE_CHECK([for printf format archetype], pgac_cv_printf_archetype, [ac_save_c_werror_flag=$ac_c_werror_flag --- 19,28 ---- # PGAC_C_PRINTF_ARCHETYPE # ----------------------- ! # Set the format archetype used by gcc to check elog/ereport functions. ! # This should accept %m, whether or not the platform's printf does. ! # We use "gnu_printf" if possible, which does that, although in some cases ! # it might do more than we could wish. AC_DEFUN([PGAC_PRINTF_ARCHETYPE], [AC_CACHE_CHECK([for printf format archetype], pgac_cv_printf_archetype, [ac_save_c_werror_flag=$ac_c_werror_flag *************** __attribute__((format(gnu_printf, 2, 3)) *** 34,41 **** [pgac_cv_printf_archetype=gnu_printf], [pgac_cv_printf_archetype=printf]) ac_c_werror_flag=$ac_save_c_werror_flag]) ! AC_DEFINE_UNQUOTED([PG_PRINTF_ATTRIBUTE], [$pgac_cv_printf_archetype], ! [Define to gnu_printf if compiler supports it, else printf.]) ])# PGAC_PRINTF_ARCHETYPE --- 34,41 ---- [pgac_cv_printf_archetype=gnu_printf], [pgac_cv_printf_archetype=printf]) ac_c_werror_flag=$ac_save_c_werror_flag]) ! AC_DEFINE_UNQUOTED([PG_PRINTF_ATTRIBUTE_M], [$pgac_cv_printf_archetype], ! [Define as a format archetype that accepts %m, if available, else printf.]) ])# PGAC_PRINTF_ARCHETYPE diff --git a/configure b/configure index 2665213..d4b4742 100755 *** a/configure --- b/configure *************** fi *** 13394,13400 **** $as_echo "$pgac_cv_printf_archetype" >&6; } cat >>confdefs.h <<_ACEOF ! #define PG_PRINTF_ATTRIBUTE $pgac_cv_printf_archetype _ACEOF --- 13394,13400 ---- $as_echo "$pgac_cv_printf_archetype" >&6; } cat >>confdefs.h <<_ACEOF ! #define PG_PRINTF_ATTRIBUTE_M $pgac_cv_printf_archetype _ACEOF diff --git a/src/include/c.h b/src/include/c.h index 1e50103..0a4757e 100644 *** a/src/include/c.h --- b/src/include/c.h *************** *** 126,135 **** /* GCC and XLC support format attributes */ #if defined(__GNUC__) || defined(__IBMC__) #define pg_attribute_format_arg(a) __attribute__((format_arg(a))) ! #define pg_attribute_printf(f,a) __attribute__((format(PG_PRINTF_ATTRIBUTE, f, a))) #else #define pg_attribute_format_arg(a) #define pg_attribute_printf(f,a) #endif /* GCC, Sunpro and XLC support aligned, packed and noreturn */ --- 126,139 ---- /* GCC and XLC support format attributes */ #if defined(__GNUC__) || defined(__IBMC__) #define pg_attribute_format_arg(a) __attribute__((format_arg(a))) ! /* Use for functions wrapping stdio's printf, which often doesn't take %m: */ ! #define pg_attribute_printf(f,a) __attribute__((format(printf, f, a))) ! /* Use for elog/ereport, which implement %m for themselves: */ ! #define pg_attribute_printf_m(f,a) __attribute__((format(PG_PRINTF_ATTRIBUTE_M, f, a))) #else #define pg_attribute_format_arg(a) #define pg_attribute_printf(f,a) + #define pg_attribute_printf_m(f,a) #endif /* GCC, Sunpro and XLC support aligned, packed and noreturn */ diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index b7e4696..05775a3 100644 *** a/src/include/pg_config.h.in --- b/src/include/pg_config.h.in *************** *** 809,816 **** /* PostgreSQL major version as a string */ #undef PG_MAJORVERSION ! /* Define to gnu_printf if compiler supports it, else printf. */ ! #undef PG_PRINTF_ATTRIBUTE /* PostgreSQL version as a string */ #undef PG_VERSION --- 809,816 ---- /* PostgreSQL major version as a string */ #undef PG_MAJORVERSION ! /* Define as a format archetype that accepts %m, if available, else printf. */ ! #undef PG_PRINTF_ATTRIBUTE_M /* PostgreSQL version as a string */ #undef PG_VERSION diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h index 7a9ba7f..4f4091d 100644 *** a/src/include/utils/elog.h --- b/src/include/utils/elog.h *************** extern int errcode(int sqlerrcode); *** 133,157 **** extern int errcode_for_file_access(void); extern int errcode_for_socket_access(void); ! extern int errmsg(const char *fmt,...) pg_attribute_printf(1, 2); ! extern int errmsg_internal(const char *fmt,...) pg_attribute_printf(1, 2); extern int errmsg_plural(const char *fmt_singular, const char *fmt_plural, ! unsigned long n,...) pg_attribute_printf(1, 4) pg_attribute_printf(2, 4); ! extern int errdetail(const char *fmt,...) pg_attribute_printf(1, 2); ! extern int errdetail_internal(const char *fmt,...) pg_attribute_printf(1, 2); ! extern int errdetail_log(const char *fmt,...) pg_attribute_printf(1, 2); extern int errdetail_log_plural(const char *fmt_singular, const char *fmt_plural, ! unsigned long n,...) pg_attribute_printf(1, 4) pg_attribute_printf(2, 4); extern int errdetail_plural(const char *fmt_singular, const char *fmt_plural, ! unsigned long n,...) pg_attribute_printf(1, 4) pg_attribute_printf(2, 4); ! extern int errhint(const char *fmt,...) pg_attribute_printf(1, 2); /* * errcontext() is typically called in error context callback functions, not --- 133,157 ---- extern int errcode_for_file_access(void); extern int errcode_for_socket_access(void); ! extern int errmsg(const char *fmt,...) pg_attribute_printf_m(1, 2); ! extern int errmsg_internal(const char *fmt,...) pg_attribute_printf_m(1, 2); extern int errmsg_plural(const char *fmt_singular, const char *fmt_plural, ! unsigned long n,...) pg_attribute_printf_m(1, 4) pg_attribute_printf_m(2, 4); ! extern int errdetail(const char *fmt,...) pg_attribute_printf_m(1, 2); ! extern int errdetail_internal(const char *fmt,...) pg_attribute_printf_m(1, 2); ! extern int errdetail_log(const char *fmt,...) pg_attribute_printf_m(1, 2); extern int errdetail_log_plural(const char *fmt_singular, const char *fmt_plural, ! unsigned long n,...) pg_attribute_printf_m(1, 4) pg_attribute_printf_m(2, 4); extern int errdetail_plural(const char *fmt_singular, const char *fmt_plural, ! unsigned long n,...) pg_attribute_printf_m(1, 4) pg_attribute_printf_m(2, 4); ! extern int errhint(const char *fmt,...) pg_attribute_printf_m(1, 2); /* * errcontext() is typically called in error context callback functions, not *************** extern int errhint(const char *fmt,...) *** 165,171 **** extern int set_errcontext_domain(const char *domain); ! extern int errcontext_msg(const char *fmt,...) pg_attribute_printf(1, 2); extern int errhidestmt(bool hide_stmt); extern int errhidecontext(bool hide_ctx); --- 165,171 ---- extern int set_errcontext_domain(const char *domain); ! extern int errcontext_msg(const char *fmt,...) pg_attribute_printf_m(1, 2); extern int errhidestmt(bool hide_stmt); extern int errhidecontext(bool hide_ctx); *************** extern int getinternalerrposition(void); *** 222,234 **** #endif /* HAVE__VA_ARGS */ extern void elog_start(const char *filename, int lineno, const char *funcname); ! extern void elog_finish(int elevel, const char *fmt,...) pg_attribute_printf(2, 3); /* Support for constructing error strings separately from ereport() calls */ extern void pre_format_elog_string(int errnumber, const char *domain); ! extern char *format_elog_string(const char *fmt,...) pg_attribute_printf(1, 2); /* Support for attaching context information to error reports */ --- 222,234 ---- #endif /* HAVE__VA_ARGS */ extern void elog_start(const char *filename, int lineno, const char *funcname); ! extern void elog_finish(int elevel, const char *fmt,...) pg_attribute_printf_m(2, 3); /* Support for constructing error strings separately from ereport() calls */ extern void pre_format_elog_string(int errnumber, const char *domain); ! extern char *format_elog_string(const char *fmt,...) pg_attribute_printf_m(1, 2); /* Support for attaching context information to error reports */ *************** extern void set_syslog_parameters(const *** 407,415 **** #endif /* ! * Write errors to stderr (or by equal means when stderr is ! * not available). Used before ereport/elog can be used ! * safely (memory context, GUC load etc) */ extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2); --- 407,415 ---- #endif /* ! * Write errors to stderr (or by comparable means when stderr is not ! * available). Used before ereport/elog can be used safely (memory context, ! * GUC load etc). Note that this does *not* accept "%m". */ extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2);
diff --git a/src/common/exec.c b/src/common/exec.c index 4df16cd..c207c02 100644 --- a/src/common/exec.c +++ b/src/common/exec.c @@ -124,8 +124,10 @@ find_my_exec(const char *argv0, char *retpath) if (!getcwd(cwd, MAXPGPATH)) { + int save_errno = errno; + log_error(_("could not identify current directory: %s"), - strerror(errno)); + strerror(save_errno)); return -1; } @@ -238,8 +240,10 @@ resolve_symlinks(char *path) */ if (!getcwd(orig_wd, MAXPGPATH)) { + int save_errno = errno; + log_error(_("could not identify current directory: %s"), - strerror(errno)); + strerror(save_errno)); return -1; } @@ -254,7 +258,10 @@ resolve_symlinks(char *path) *lsep = '\0'; if (chdir(path) == -1) { - log_error4(_("could not change directory to \"%s\": %s"), path, strerror(errno)); + int save_errno = errno; + + log_error4(_("could not change directory to \"%s\": %s"), + path, strerror(save_errno)); return -1; } fname = lsep + 1; @@ -281,8 +288,10 @@ resolve_symlinks(char *path) if (!getcwd(path, MAXPGPATH)) { + int save_errno = errno; + log_error(_("could not identify current directory: %s"), - strerror(errno)); + strerror(save_errno)); return -1; } join_path_components(path, path, link_buf); @@ -290,7 +299,10 @@ resolve_symlinks(char *path) if (chdir(orig_wd) == -1) { - log_error4(_("could not change directory to \"%s\": %s"), orig_wd, strerror(errno)); + int save_errno = errno; + + log_error4(_("could not change directory to \"%s\": %s"), + orig_wd, strerror(save_errno)); return -1; } #endif /* HAVE_READLINK */ @@ -520,7 +532,10 @@ pclose_check(FILE *stream) if (exitstatus == -1) { /* pclose() itself failed, and hopefully set errno */ - log_error(_("pclose failed: %s"), strerror(errno)); + int save_errno = errno; + + log_error(_("pclose failed: %s"), + strerror(save_errno)); } else { diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h index 7a9ba7f..4350b12 100644 --- a/src/include/utils/elog.h +++ b/src/include/utils/elog.h @@ -70,6 +70,23 @@ /* SQLSTATE codes for errors are defined in a separate file */ #include "utils/errcodes.h" +/* + * Provide a way to prevent "errno" from being accidentally used inside an + * elog() or ereport() invocation. Since we know that some operating systems + * define errno as something involving a function call, we'll put a local + * variable of the same name as that function in the local scope to force a + * compile error. On platforms that don't define errno in that way, nothing + * happens, so we get no warning ... but we can live with that as long as it + * happens on some popular platforms. + */ +#if defined(errno) && defined(__linux__) +#define pg_prevent_errno_in_scope() int __errno_location pg_attribute_unused() +#elif defined(errno) && (defined(__darwin__) || defined(__freebsd__)) +#define pg_prevent_errno_in_scope() int __error pg_attribute_unused() +#else +#define pg_prevent_errno_in_scope() +#endif + /*---------- * New-style error reporting API: to be used in this way: @@ -103,6 +120,7 @@ #ifdef HAVE__BUILTIN_CONSTANT_P #define ereport_domain(elevel, domain, rest) \ do { \ + pg_prevent_errno_in_scope(); \ if (errstart(elevel, __FILE__, __LINE__, PG_FUNCNAME_MACRO, domain)) \ errfinish rest; \ if (__builtin_constant_p(elevel) && (elevel) >= ERROR) \ @@ -112,6 +130,7 @@ #define ereport_domain(elevel, domain, rest) \ do { \ const int elevel_ = (elevel); \ + pg_prevent_errno_in_scope(); \ if (errstart(elevel_, __FILE__, __LINE__, PG_FUNCNAME_MACRO, domain)) \ errfinish rest; \ if (elevel_ >= ERROR) \ @@ -198,6 +217,7 @@ extern int getinternalerrposition(void); #ifdef HAVE__BUILTIN_CONSTANT_P #define elog(elevel, ...) \ do { \ + pg_prevent_errno_in_scope(); \ elog_start(__FILE__, __LINE__, PG_FUNCNAME_MACRO); \ elog_finish(elevel, __VA_ARGS__); \ if (__builtin_constant_p(elevel) && (elevel) >= ERROR) \ @@ -206,6 +226,7 @@ extern int getinternalerrposition(void); #else /* !HAVE__BUILTIN_CONSTANT_P */ #define elog(elevel, ...) \ do { \ + pg_prevent_errno_in_scope(); \ elog_start(__FILE__, __LINE__, PG_FUNCNAME_MACRO); \ { \ const int elevel_ = (elevel); \