This month I ended up with Text::BibTex. Conveniently, this distro came with an actual bug that needed fixing.
This, however, was not a Perl problem at all but rather a C problem. The module was causing an actual crash with this stack trace:
/lib/x86_64-linux-gnu/libc.so.6(__fortify_fail+0x37)[0x7f9f39cc5f37] /lib/x86_64-linux-gnu/libc.so.6(+0xebdf0)[0x7f9f39cc4df0] /lib/x86_64-linux-gnu/libc.so.6(+0xead37)[0x7f9f39cc3d37] /usr/lib/libbtparse.so.1(zzFAIL+0xe4)[0x7f9f38eabdc4] /usr/lib/libbtparse.so.1(body+0xdf)[0x7f9f38eab56f] /usr/lib/libbtparse.so.1(entry+0x1ea)[0x7f9f38eab98a] /usr/lib/libbtparse.so.1(bt_parse_entry+0x100)[0x7f9f38ea9d40] /usr/lib/perl5/auto/Text/BibTeX/BibTeX.so(XS_Text__BibTeX__Entry__parse+0x 135)[0x7f9f390c1a45] /usr/lib/libperl.so.5.14(Perl_pp_entersub+0x58c)[0x7f9f3a6ba3cc] /usr/lib/libperl.so.5.14(Perl_runops_standard+0x16)[0x7f9f3a6b19a6] /usr/lib/libperl.so.5.14(perl_run+0x3a5)[0x7f9f3a6535b5] /usr/bin/perl(main+0x149)[0x400f89] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xfd)[0x7f9f39bf7ead] /usr/bin/perl[0x400fc1]
I don’t actually know anything about BibTex, so the first thing I did was strip down the problem data to the smallest thing that would still break and make a test case. As it turned out, the smallest thing was about 2000 bytes of any text inside a comment that wasn’t closed. This was a bit of a clue:
./btparse/pccts/antlr.h:74:#define ZZLEXBUFSIZE 2000
The culprit in this case was an improper use of strncat() in this block:
#ifdef LL_K
static char text[LL_K*ZZLEXBUFSIZE+1];
SetWordType *f[LL_K];
#else
static char text[ZZLEXBUFSIZE+1];
SetWordType *f[1];
#endif
SetWordType **miss_set;
char **miss_text;
int *bad_tok;
char **bad_text;
int *err_k;
int i;
va_list ap;
#ifndef __USE_PROTOS
int k;
#endif
#ifdef __USE_PROTOS
va_start(ap, k);
#else
va_start(ap);
k = va_arg(ap, int); /* how many lookahead sets? */
#endif
text[0] = '\0';
for (i=1; i<=k; i++) /* collect all lookahead sets */
{
f[i-1] = va_arg(ap, SetWordType *);
}
for (i=1; i<=k; i++) /* look for offending token */
{
#ifdef LL_K
int freeSpace = (LL_K*ZZLEXBUFSIZE+1) - strlen(text);
#else
int freeSpace = (ZZLEXBUFSIZE+1) - strlen(text);
#endif
if ( i>1 ) strcat(text, " ");
strncat(text, LATEXT(i), freeSpace);
if ( !zzset_el((unsigned)LA(i), f[i-1]) ) break;
}
strncat(a,b,n) looks like it safely avoids overflowing the destination buffer but in fact according to its specification:
If src contains n or more bytes, strncat() writes n+1 bytes to dest (n from src plus the terminating null byte). Therefore, the size of dest must be at least strlen(dest)+n+1.
So to fix, we just allocate more space:
#ifdef LL_K static char text[LL_K*ZZLEXBUFSIZE+1+1]; SetWordType *f[LL_K]; #else static char text[ZZLEXBUFSIZE+1+1]; SetWordType *f[1]; #endif