summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--perl.h46
-rw-r--r--perlio.c2
-rw-r--r--pod/perlapi.pod14
-rw-r--r--util.c96
4 files changed, 93 insertions, 65 deletions
diff --git a/perl.h b/perl.h
index 91e1b58647..2cf5a46f8b 100644
--- a/perl.h
+++ b/perl.h
@@ -348,11 +348,17 @@ register struct op *Perl_op asm(stringify(OP_IN_REGISTER));
#endif
#if defined(PERL_GCC_PEDANTIC)
-# if !defined(PERL_GCC_BRACE_GROUPS_FORBIDDEN)
+# ifndef PERL_GCC_BRACE_GROUPS_FORBIDDEN
# define PERL_GCC_BRACE_GROUPS_FORBIDDEN
# endif
#endif
+#if defined(__GNUC__) && !defined(PERL_GCC_BRACE_GROUPS_FORBIDDEN) && !defined(__cplusplus)
+# ifndef PERL_USE_GCC_BRACE_GROUPS
+# define PERL_USE_GCC_BRACE_GROUPS
+# endif
+#endif
+
/*
* STMT_START { statements; } STMT_END;
* can be used as a single statement, as in
@@ -361,7 +367,7 @@ register struct op *Perl_op asm(stringify(OP_IN_REGISTER));
* Trying to select a version that gives no warnings...
*/
#if !(defined(STMT_START) && defined(STMT_END))
-# if defined(__GNUC__) && !defined(PERL_GCC_BRACE_GROUPS_FORBIDDEN) && !defined(__cplusplus)
+# ifdef PERL_USE_GCC_BRACE_GROUPS
# define STMT_START (void)( /* gcc supports "({ STATEMENTS; })" */
# define STMT_END )
# else
@@ -1439,18 +1445,38 @@ int sockatmark(int);
*/
#ifdef SPRINTF_RETURNS_STRLEN
# define my_sprintf sprintf
-# ifdef HAS_SNPRINTF
-# define USE_SNPRINTF
-# endif
-# ifdef HAS_VSNPRINTF
-# define USE_VSNPRINTF
-# endif
#else
# define my_sprintf Perl_my_sprintf
#endif
-#define my_snprintf Perl_my_snprintf
-#define my_vsnprintf Perl_my_vsnprintf
+/*
+ * If we have v?snprintf() and the C99 variadic macros, we can just
+ * use just the v?snprintf(). It is nice to try to trap the buffer
+ * overflow, however, so if we are DEBUGGING, and we cannot use the
+ * gcc brace groups, then use the function wrappers which try to trap
+ * the overflow. If we can use the gcc brace groups, we can try that
+ * even with the version that uses the C99 variadic macros.
+ */
+
+#if defined(HAS_C99_VARIADIC_MACROS) && !(defined(DEBUGGING) && !defined(PERL_USE_GCC_BRACE_GROUPS))
+# ifdef PERL_USE_GCC_BRACE_GROUPS
+# define my_snprintf(buffer, len, ...) ({ int __len__ = snprintf(buffer, len, __VA_ARGS__); if (__len__ >= len) Perl_croak(aTHX_ "panic: snprintf buffer overflow"); __len__; })
+# else
+# define my_snprintf(buffer, len, ...) snprintf(buffer, len, __VA_ARGS__)
+# endif
+#else
+# define my_snprintf Perl_my_snprintf
+#endif
+
+#if defined(HAS_C99_VARIADIC_MACROS)
+# ifdef PERL_USE_GCC_BRACE_GROUPS
+# define my_vsnprintf(buffer, len, ...) ({ int __len__ = vsnprintf(buffer, len, __VA_ARGS__); if (__len__ >= len) Perl_croak(aTHX_ "panic: vsnprintf buffer overflow"); __len__; })
+# else
+# define my_vsnprintf(buffer, len, ...) vsnprintf(buffer, len, __VA_ARGS__)
+# endif
+#else
+# define my_vsnprintf Perl_my_vsnprintf
+#endif
/* Configure gets this right but the UTS compiler gets it wrong.
-- Hal Morris <hom00@utsglobal.com> */
diff --git a/perlio.c b/perlio.c
index ad2570e62f..9e06ef1856 100644
--- a/perlio.c
+++ b/perlio.c
@@ -5142,7 +5142,7 @@ vfprintf(FILE *fd, char *pat, char *args)
int
PerlIO_vsprintf(char *s, int n, const char *fmt, va_list ap)
{
- dVAR;
+ dTHX;
const int val = my_vsnprintf(s, n > 0 ? n : 0, fmt, ap);
if (n >= 0) {
if (strlen(s) >= (STRLEN) n) {
diff --git a/pod/perlapi.pod b/pod/perlapi.pod
index 400b329ca1..b9eb329b71 100644
--- a/pod/perlapi.pod
+++ b/pod/perlapi.pod
@@ -2249,9 +2249,11 @@ Found in file util.c
X<my_snprintf>
The C library C<snprintf> functionality, if available and
-standards-compliant (uses C<vsnprintf>, actually). If the
+standards-compliant (uses C<vsnprintf>, actually). However, if the
C<vsnprintf> is not available, will unfortunately use the unsafe
-C<vsprintf>. Consider using C<sv_vcatpvf> instead.
+C<vsprintf> which can overrun the buffer (there is an overrun check,
+but that may be too late). Consider using C<sv_vcatpvf> instead, or
+getting C<vsnprintf>.
int my_snprintf(char *buffer, const Size_t len, const char *format, ...)
@@ -2273,9 +2275,11 @@ Found in file util.c
=item my_vsnprintf
X<my_vsnprintf>
-The C library C<vsnprintf> if available and standards-compliant,
-but if the C<vsnprintf> is not available, will unfortunately use
-the unsafe C<vsprintf>. Consider using C<sv_vcatpvf> instead.
+The C library C<vsnprintf> if available and standards-compliant.
+However, if if the C<vsnprintf> is not available, will unfortunately
+use the unsafe C<vsprintf> which can overrun the buffer (there is an
+overrun check, but that may be too late). Consider using
+C<sv_vcatpvf> instead, or getting C<vsnprintf>.
int my_vsnprintf(char *buffer, const Size_t len, const char *format, va_list ap)
diff --git a/util.c b/util.c
index 82f7f82b00..34746601c4 100644
--- a/util.c
+++ b/util.c
@@ -5206,27 +5206,30 @@ Perl_mem_log_alloc(const UV n, const UV typesize, const char *typename, Malloc_t
/* We can't use SVs or PerlIO for obvious reasons,
* so we'll use stdio and low-level IO instead. */
char buf[PERL_MEM_LOG_SPRINTF_BUF_SIZE];
-# if defined(PERL_MEM_LOG_TIMESTAMP) && defined(HAS_GETTIMEOFDAY)
+# ifdef PERL_MEM_LOG_TIMESTAMP
struct timeval tv;
+# ifdef HAS_GETTIMEOFDAY
gettimeofday(&tv, 0);
+# endif
+ /* If there are other OS specific ways of hires time than
+ * gettimeofday() (see ext/Time/HiRes), the easiest way is
+ * probably that they would be used to fill in the struct
+ * timeval. */
+# endif
{
const STRLEN len =
my_snprintf(buf,
PERL_MEM_LOG_SPRINTF_BUF_SIZE,
- "%10d.%06d: alloc: %s:%d:%s: %"IVdf" %"UVuf
- " %s = %"IVdf": %"UVxf"\n",
- (int)tv.tv_sec, (int)tv.tv_usec,
- filename, linenumber, funcname, n, typesize,
- typename, n * typesize, PTR2UV(newalloc));
-# else
- const STRLEN len =
- my_snprintf(buf,
- PERL_MEM_LOG_SPRINTF_BUF_SIZE,
+# ifdef PERL_MEM_LOG_TIMESTAMP
+ "%10d.%06d: "
+# endif
"alloc: %s:%d:%s: %"IVdf" %"UVuf
" %s = %"IVdf": %"UVxf"\n",
+# ifdef PERL_MEM_LOG_TIMESTAMP
+ (int)tv.tv_sec, (int)tv.tv_usec,
+# endif
filename, linenumber, funcname, n, typesize,
typename, n * typesize, PTR2UV(newalloc));
-# endif
# ifdef PERL_MEM_LOG_ENV_FD
s = PerlEnv_getenv("PERL_MEM_LOG_FD");
PerlLIO_write(s ? atoi(s) : PERL_MEM_LOG_FD, buf, len);
@@ -5254,29 +5257,25 @@ Perl_mem_log_realloc(const UV n, const UV typesize, const char *typename, Malloc
/* We can't use SVs or PerlIO for obvious reasons,
* so we'll use stdio and low-level IO instead. */
char buf[PERL_MEM_LOG_SPRINTF_BUF_SIZE];
-# if defined(PERL_MEM_LOG_TIMESTAMP) && defined(HAS_GETTIMEOFDAY)
+# ifdef PERL_MEM_LOG_TIMESTAMP
struct timeval tv;
gettimeofday(&tv, 0);
+# endif
{
const STRLEN len =
my_snprintf(buf,
PERL_MEM_LOG_SPRINTF_BUF_SIZE,
- "%10d.%06d: realloc: %s:%d:%s: %"IVdf" %"UVuf
- " %s = %"IVdf": %"UVxf" -> %"UVxf"\n",
- (int)tv.tv_sec, (int)tv.tv_usec,
- filename, linenumber, funcname, n, typesize,
- typename, n * typesize, PTR2UV(oldalloc),
- PTR2UV(newalloc));
-# else
- const STRLEN len =
- my_snprintf(buf,
- PERL_MEM_LOG_SPRINTF_BUF_SIZE,
+# ifdef PERL_MEM_LOG_TIMESTAMP
+ "%10d.%06d: "
+# endif
"realloc: %s:%d:%s: %"IVdf" %"UVuf
" %s = %"IVdf": %"UVxf" -> %"UVxf"\n",
+# ifdef PERL_MEM_LOG_TIMESTAMP
+ (int)tv.tv_sec, (int)tv.tv_usec,
+# endif
filename, linenumber, funcname, n, typesize,
typename, n * typesize, PTR2UV(oldalloc),
PTR2UV(newalloc));
-# endif
# ifdef PERL_MEM_LOG_ENV_FD
s = PerlEnv_getenv("PERL_MEM_LOG_FD");
PerlLIO_write(s ? atoi(s) : PERL_MEM_LOG_FD, buf, len);
@@ -5304,24 +5303,23 @@ Perl_mem_log_free(Malloc_t oldalloc, const char *filename, const int linenumber,
/* We can't use SVs or PerlIO for obvious reasons,
* so we'll use stdio and low-level IO instead. */
char buf[PERL_MEM_LOG_SPRINTF_BUF_SIZE];
-# if defined(PERL_MEM_LOG_TIMESTAMP) && defined(HAS_GETTIMEOFDAY)
+# ifdef PERL_MEM_LOG_TIMESTAMP
struct timeval tv;
gettimeofday(&tv, 0);
+# endif
{
const STRLEN len =
my_snprintf(buf,
PERL_MEM_LOG_SPRINTF_BUF_SIZE,
- "%10d.%06d: free: %s:%d:%s: %"UVxf"\n",
+# ifdef PERL_MEM_LOG_TIMESTAMP
+ "%10d.%06d: "
+# endif
+ "free: %s:%d:%s: %"UVxf"\n",
+# ifdef PERL_MEM_LOG_TIMESTAMP
(int)tv.tv_sec, (int)tv.tv_usec,
+# endif
filename, linenumber, funcname,
PTR2UV(oldalloc));
-# else
- const STRLEN len =
- my_sprintf(buf,
- "free: %s:%d:%s: %"UVxf"\n",
- filename, linenumber, funcname,
- PTR2UV(oldalloc));
-# endif
# ifdef PERL_MEM_LOG_ENV_FD
s = PerlEnv_getenv("PERL_MEM_LOG_FD");
PerlLIO_write(s ? atoi(s) : PERL_MEM_LOG_FD, buf, len);
@@ -5361,65 +5359,65 @@ Perl_my_sprintf(char *buffer, const char* pat, ...)
=for apidoc my_snprintf
The C library C<snprintf> functionality, if available and
-standards-compliant (uses C<vsnprintf>, actually). If the
+standards-compliant (uses C<vsnprintf>, actually). However, if the
C<vsnprintf> is not available, will unfortunately use the unsafe
-C<vsprintf>. Consider using C<sv_vcatpvf> instead.
+C<vsprintf> which can overrun the buffer (there is an overrun check,
+but that may be too late). Consider using C<sv_vcatpvf> instead, or
+getting C<vsnprintf>.
=cut
*/
int
Perl_my_snprintf(char *buffer, const Size_t len, const char *format, ...)
-/* Cannot do this using variadic macros because that is too unportable. */
{
dTHX;
int retval;
va_list ap;
-#ifndef USE_VSNPRINTF
- PERL_UNUSED_ARG(len);
-#endif
va_start(ap, format);
-#ifdef USE_VSNPRINTF
+#ifdef HAS_VSNPRINTF
retval = vsnprintf(buffer, len, format, ap);
#else
retval = vsprintf(buffer, format, ap);
#endif
va_end(ap);
+ if (retval >= len)
+ Perl_croak(aTHX_ "panic: my_snprintf buffer overflow");
return retval;
}
/*
=for apidoc my_vsnprintf
-The C library C<vsnprintf> if available and standards-compliant,
-but if the C<vsnprintf> is not available, will unfortunately use
-the unsafe C<vsprintf>. Consider using C<sv_vcatpvf> instead.
+The C library C<vsnprintf> if available and standards-compliant.
+However, if if the C<vsnprintf> is not available, will unfortunately
+use the unsafe C<vsprintf> which can overrun the buffer (there is an
+overrun check, but that may be too late). Consider using
+C<sv_vcatpvf> instead, or getting C<vsnprintf>.
=cut
*/
int
Perl_my_vsnprintf(char *buffer, const Size_t len, const char *format, va_list ap)
-/* Cannot do this using variadic macros because that is too unportable. */
{
dTHX;
int retval;
-#ifndef USE_VSNPRINTF
- PERL_UNUSED_ARG(len);
-#endif
#ifdef NEED_VA_COPY
va_list apc;
Perl_va_copy(apc);
-# ifdef USE_VSNPRINTF
+# ifdef HAS_VSNPRINTF
retval = vsnprintf(buffer, len, format, apc);
# else
retval = vsprintf(buffer, format, apc);
# endif
#else
-# ifdef USE_VSNPRINTF
+# ifdef HAS_VSNPRINTF
retval = vsnprintf(buffer, len, format, ap);
# else
retval = vsprintf(buffer, format, ap);
# endif
-#endif
+#endif /* #ifdef NEED_VA_COPY */
+ if (retval >= len)
+ Perl_croak(aTHX_ "panic: my_vsnprintf buffer overflow");
return retval;
}