Fix regerror() small buffer issues.

This commit is contained in:
Philip.Hazel 2015-10-30 17:17:40 +00:00
parent 30170ade3d
commit 4b242c4c78
6 changed files with 49 additions and 24 deletions

View File

@ -226,6 +226,9 @@ overflow.
make it easier to test long repetitive patterns. The tests for 63 above are make it easier to test long repetitive patterns. The tests for 63 above are
converted to use the new feature. converted to use the new feature.
66. In the POSIX wrapper, if regerror() was given too small a buffer, it could
misbehave.
Version 10.20 30-June-2015 Version 10.20 30-June-2015
-------------------------- --------------------------

View File

@ -1,4 +1,4 @@
.TH PCRE2POSIX 3 "03 September 2015" "PCRE2 10.21" .TH PCRE2POSIX 3 "30 October 2015" "PCRE2 10.21"
.SH NAME .SH NAME
PCRE2 - Perl-compatible regular expressions (revised API) PCRE2 - Perl-compatible regular expressions (revised API)
.SH "SYNOPSIS" .SH "SYNOPSIS"
@ -240,9 +240,11 @@ header file, of which REG_NOMATCH is the "expected" failure code.
The \fBregerror()\fP function maps a non-zero errorcode from either The \fBregerror()\fP function maps a non-zero errorcode from either
\fBregcomp()\fP or \fBregexec()\fP to a printable message. If \fIpreg\fP is not \fBregcomp()\fP or \fBregexec()\fP to a printable message. If \fIpreg\fP is not
NULL, the error should have arisen from the use of that structure. A message NULL, the error should have arisen from the use of that structure. A message
terminated by a binary zero is placed in \fIerrbuf\fP. The length of the terminated by a binary zero is placed in \fIerrbuf\fP. If the buffer is too
message, including the zero, is limited to \fIerrbuf_size\fP. The yield of the short, only the first \fIerrbuf_size\fP - 1 characters of the error message are
function is the size of buffer needed to hold the whole message. used. The yield of the function is the size of buffer needed to hold the whole
message, including the terminating zero. This value is greater than
\fIerrbuf_size\fP if the message was truncated.
. .
. .
.SH MEMORY USAGE .SH MEMORY USAGE
@ -267,6 +269,6 @@ Cambridge, England.
.rs .rs
.sp .sp
.nf .nf
Last updated: 03 September 2015 Last updated: 30 October 2015
Copyright (c) 1997-2015 University of Cambridge. Copyright (c) 1997-2015 University of Cambridge.
.fi .fi

View File

@ -144,29 +144,23 @@ static const char *const pstring[] = {
PCRE2POSIX_EXP_DEFN size_t PCRE2_CALL_CONVENTION PCRE2POSIX_EXP_DEFN size_t PCRE2_CALL_CONVENTION
regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size) regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size)
{ {
const char *message, *addmessage; int used;
size_t length, addlength; const char *message;
message = (errcode >= (int)(sizeof(pstring)/sizeof(char *)))? message = (errcode >= (int)(sizeof(pstring)/sizeof(char *)))?
"unknown error code" : pstring[errcode]; "unknown error code" : pstring[errcode];
length = strlen(message) + 1;
addmessage = " at offset "; if (preg != NULL && (int)preg->re_erroffset != -1)
addlength = (preg != NULL && (int)preg->re_erroffset != -1)?
strlen(addmessage) + 6 : 0;
if (errbuf_size > 0)
{ {
if (addlength > 0 && errbuf_size >= length + addlength) used = snprintf(errbuf, errbuf_size, "%s at offset %-6d", message,
sprintf(errbuf, "%s%s%-6d", message, addmessage, (int)preg->re_erroffset); (int)preg->re_erroffset);
else
{
strncpy(errbuf, message, errbuf_size - 1);
errbuf[errbuf_size-1] = 0;
} }
else
{
used = snprintf(errbuf, errbuf_size, "%s", message);
} }
return length + addlength; return used + 1;
} }

View File

@ -445,6 +445,7 @@ typedef struct patctl { /* Structure for pattern modifiers. */
uint32_t jit; uint32_t jit;
uint32_t stackguard_test; uint32_t stackguard_test;
uint32_t tables_id; uint32_t tables_id;
uint32_t regerror_buffsize;
uint8_t locale[LOCALESIZE]; uint8_t locale[LOCALESIZE];
} patctl; } patctl;
@ -566,6 +567,7 @@ static modstruct modlist[] = {
{ "ps", MOD_DAT, MOD_OPT, PCRE2_PARTIAL_SOFT, DO(options) }, { "ps", MOD_DAT, MOD_OPT, PCRE2_PARTIAL_SOFT, DO(options) },
{ "push", MOD_PAT, MOD_CTL, CTL_PUSH, PO(control) }, { "push", MOD_PAT, MOD_CTL, CTL_PUSH, PO(control) },
{ "recursion_limit", MOD_CTM, MOD_INT, 0, MO(recursion_limit) }, { "recursion_limit", MOD_CTM, MOD_INT, 0, MO(recursion_limit) },
{ "regerror_buffsize", MOD_PAT, MOD_INT, 0, PO(regerror_buffsize) },
{ "replace", MOD_PND, MOD_STR, REPLACE_MODSIZE, PO(replacement) }, { "replace", MOD_PND, MOD_STR, REPLACE_MODSIZE, PO(replacement) },
{ "stackguard", MOD_PAT, MOD_INT, 0, PO(stackguard_test) }, { "stackguard", MOD_PAT, MOD_INT, 0, PO(stackguard_test) },
{ "startchar", MOD_PND, MOD_CTL, CTL_STARTCHAR, PO(control) }, { "startchar", MOD_PND, MOD_CTL, CTL_STARTCHAR, PO(control) },
@ -774,7 +776,7 @@ buffer is where all input lines are read. Its size is the same as pbuffer8.
Pattern lines are always copied to pbuffer8 for use in callouts, even if they Pattern lines are always copied to pbuffer8 for use in callouts, even if they
are actually compiled from pbuffer16 or pbuffer32. */ are actually compiled from pbuffer16 or pbuffer32. */
static int pbuffer8_size = 50000; /* Initial size, bytes */ static size_t pbuffer8_size = 50000; /* Initial size, bytes */
static uint8_t *pbuffer8 = NULL; static uint8_t *pbuffer8 = NULL;
static uint8_t *buffer = NULL; static uint8_t *buffer = NULL;
@ -4575,8 +4577,21 @@ if ((pat_patctl.control & CTL_POSIX) != 0)
rc = regcomp(&preg, (char *)pbuffer8, cflags); rc = regcomp(&preg, (char *)pbuffer8, cflags);
if (rc != 0) /* Failure */ if (rc != 0) /* Failure */
{ {
(void)regerror(rc, &preg, (char *)pbuffer8, pbuffer8_size); size_t bsize, usize;
bsize = (pat_patctl.regerror_buffsize != 0)?
pat_patctl.regerror_buffsize : pbuffer8_size;
if (bsize + 8 < pbuffer8_size)
memcpy(pbuffer8 + bsize, "DEADBEEF", 8);
usize = regerror(rc, &preg, (char *)pbuffer8, bsize);
fprintf(outfile, "Failed: POSIX code %d: %s\n", rc, pbuffer8); fprintf(outfile, "Failed: POSIX code %d: %s\n", rc, pbuffer8);
if (usize > bsize)
{
fprintf(outfile, "** regerror() message truncated\n");
if (memcmp(pbuffer8 + bsize, "DEADBEEF", 8) != 0)
fprintf(outfile, "** regerror() buffer overflow\n");
}
return PR_SKIP; return PR_SKIP;
} }
return PR_OK; return PR_OK;

View File

@ -94,4 +94,8 @@
/abcd/substitute_extended /abcd/substitute_extended
/\[A]{1000000}**/expand,regerror_buffsize=31
/\[A]{1000000}**/expand,regerror_buffsize=32
# End of testdata/testinput18 # End of testdata/testinput18

View File

@ -143,4 +143,11 @@ Failed: POSIX code 3: pattern error at offset 2
/abcd/substitute_extended /abcd/substitute_extended
** Ignored with POSIX interface: substitute_extended ** Ignored with POSIX interface: substitute_extended
/\[A]{1000000}**/expand,regerror_buffsize=31
Failed: POSIX code 4: ? * + invalid at offset 100000
** regerror() message truncated
/\[A]{1000000}**/expand,regerror_buffsize=32
Failed: POSIX code 4: ? * + invalid at offset 1000001
# End of testdata/testinput18 # End of testdata/testinput18