Commit: ccfilter: buffer overflow in ccfilter.c with crafted compiler output

1 view
Skip to first unread message

Christian Brabandt

unread,
Jun 20, 2026, 10:30:12 AM (yesterday) Jun 20
to vim...@googlegroups.com
ccfilter: buffer overflow in ccfilter.c with crafted compiler output

Commit: https://github.com/vim/vim/commit/a092d249b6066401dda0964ceeb213062785bbc8
Author: Hirohito Higashi <h.eas...@gmail.com>
Date: Thu Jun 18 20:12:48 2026 +0000

ccfilter: buffer overflow in ccfilter.c with crafted compiler output

Problem: ccfilter: can overflow fixed-size buffers (FileName, BasePath,
Reason) when parsing crafted compiler output, because the
sscanf() calls read "%[" fields without a width limit (DDugs)
Solution: Give every sscanf() "%[" conversion a width limit derived from
the buffer size (Hirohito Higashi).

Co-Authored-By: Claude Opus 4.8 (1M context) <nor...@anthropic.com>

Signed-off-by: Hirohito Higashi <h.eas...@gmail.com>
Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/runtime/tools/ccfilter.c b/runtime/tools/ccfilter.c
index 269e4ee66..5fe6307bf 100644
--- a/runtime/tools/ccfilter.c
+++ b/runtime/tools/ccfilter.c
@@ -18,15 +18,24 @@
#include <string.h>
#include <unistd.h>

-#define LINELENGTH 2048
+/*
+ * sscanf() needs a literal field width on each "%[", so define the widths
+ * once and derive the buffer sizes (width + 1) and format widths (via STR()).
+ */
+#define STR_(x) #x
+#define STR(x) STR_(x)
+#define LINEWIDTH 2047
+#define LINELENGTH (LINEWIDTH + 1)
+#define PATHWIDTH 1023
+#define PATHLENGTH (PATHWIDTH + 1)

/* Collector(s) */
char Line[LINELENGTH];
char Line2[LINELENGTH];
/* Components */
-char FileName[1024];
-char BasePath[1024];
-char CWD[1024];
+char FileName[PATHLENGTH];
+char BasePath[PATHLENGTH];
+char CWD[PATHLENGTH];
unsigned long Row;
unsigned long Col;
char Severity;
@@ -184,7 +193,7 @@ int main( int argc, char *argv[] )
case COMPILER_GCC:
Severity = 'e';
#ifdef GOTO_FROM_WHERE_INCLUDED
- rv = sscanf( Line, "In file included from %[^:]:%lu:",
+ rv = sscanf( Line, "In file included from %" STR(PATHWIDTH) "[^:]:%lu:",
FileName, &Row );
if ( rv == 2 )
{
@@ -193,11 +202,11 @@ int main( int argc, char *argv[] )
else
#endif
{
- if ((rv = sscanf( Line, "%[^:]:%lu: warning: %[^
]",
+ if ((rv = sscanf( Line, "%" STR(PATHWIDTH) "[^:]:%lu: warning: %" STR(LINEWIDTH) "[^
]",
FileName, &Row, Reason ))==3) {
Severity = 'w';
} else {
- rv = sscanf( Line, "%[^:]:%lu: %[^
]",
+ rv = sscanf( Line, "%" STR(PATHWIDTH) "[^:]:%lu: %" STR(LINEWIDTH) "[^
]",
FileName, &Row, Reason );
}
ok = ( rv == 3 );
@@ -205,24 +214,24 @@ int main( int argc, char *argv[] )
Col = (dec_col ? 1 : 0 );
break;
case COMPILER_AIX:
- rv = sscanf( Line, "\"%[^\"]\", line %lu.%lu: %*s (%c) %[^
]",
+ rv = sscanf( Line, "\"%" STR(PATHWIDTH) "[^\"]\", line %lu.%lu: %*s (%c) %" STR(LINEWIDTH) "[^
]",
FileName, &Row, &Col, &Severity, Reason );
ok = ( rv == 5 );
break;
case COMPILER_HPUX:
- rv = sscanf( Line, "cc: \"%[^\"]\", line %lu: %c%*[^:]: %[^
]",
+ rv = sscanf( Line, "cc: \"%" STR(PATHWIDTH) "[^\"]\", line %lu: %c%*[^:]: %" STR(LINEWIDTH) "[^
]",
FileName, &Row, &Severity, Reason );
ok = ( rv == 4 );
Col = (dec_col ? 1 : 0 );
break;
case COMPILER_SOLARIS:
- rv = sscanf( Line, "\"%[^\"]\", line %lu: warning: %[^
]",
+ rv = sscanf( Line, "\"%" STR(PATHWIDTH) "[^\"]\", line %lu: warning: %" STR(LINEWIDTH) "[^
]",
FileName, &Row, Reason );
Severity = 'w';
ok = ( rv == 3 );
if ( rv != 3 )
{
- rv = sscanf( Line, "\"%[^\"]\", line %lu: %[^
]",
+ rv = sscanf( Line, "\"%" STR(PATHWIDTH) "[^\"]\", line %lu: %" STR(LINEWIDTH) "[^
]",
FileName, &Row, Reason );
Severity = 'e';
ok = ( rv == 3 );
@@ -230,18 +239,18 @@ int main( int argc, char *argv[] )
Col = (dec_col ? 1 : 0 );
break;
case COMPILER_ATT:
- rv = sscanf( Line, "%c \"%[^\"]\",L%lu/C%lu%*[^:]:%[^
]",
+ rv = sscanf( Line, "%c \"%" STR(PATHWIDTH) "[^\"]\",L%lu/C%lu%*[^:]:%" STR(LINEWIDTH) "[^
]",
&Severity, FileName, &Row, &Col, Reason );
ok = ( rv == 5 );

if (rv != 5)
- { rv = sscanf( Line, "%c \"%[^\"]\",L%lu/C%lu: %[^
]",
+ { rv = sscanf( Line, "%c \"%" STR(PATHWIDTH) "[^\"]\",L%lu/C%lu: %" STR(LINEWIDTH) "[^
]",
&Severity, FileName, &Row, &Col, Reason );
ok = ( rv == 5 );
}

if (rv != 5)
- { rv = sscanf( Line, "%c \"%[^\"]\",L%lu: %[^
]",
+ { rv = sscanf( Line, "%c \"%" STR(PATHWIDTH) "[^\"]\",L%lu: %" STR(LINEWIDTH) "[^
]",
&Severity, FileName, &Row, Reason );
ok = ( rv == 4 );
Col = (dec_col ? 1 : 0 );
@@ -273,10 +282,10 @@ int main( int argc, char *argv[] )
}
else
{
- rv = sscanf( p+2, "%[^:]: %lu: %[^
]",
+ rv = sscanf( p+2, "%" STR(PATHWIDTH) "[^:]: %lu: %" STR(LINEWIDTH) "[^
]",
FileName, &Row, Reason );
if (rv != 3)
- rv = sscanf( p+2, "%[^,], line %lu: %[^
]",
+ rv = sscanf( p+2, "%" STR(PATHWIDTH) "[^,], line %lu: %" STR(LINEWIDTH) "[^
]",
FileName, &Row, Reason );
ok = ( rv == 3 );
}
@@ -307,7 +316,7 @@ int main( int argc, char *argv[] )
p = &Line[1];
else
p = &Line[0];
- ok = sscanf( p, "make[%*d]: Entering directory `%[^']",
+ ok = sscanf( p, "make[%*d]: Entering directory `%" STR(PATHWIDTH) "[^']",
BasePath );
if (verbose)
printf( "[%u]?%s
", (unsigned)ok, Line );
diff --git a/runtime/tools/ccfilter_README.txt b/runtime/tools/ccfilter_README.txt
index ea989f257..70d8f3f4d 100644
--- a/runtime/tools/ccfilter_README.txt
+++ b/runtime/tools/ccfilter_README.txt
@@ -5,7 +5,8 @@ ccfilter is a C program to filter the output of a few compilers to a common
QuickFix format. It is provided with Vim to make quickfix useful for more
compilers.

-ccfilter WILL FAIL with long lines (more than 2047 bytes).
+ccfilter handles input lines up to 2047 bytes; longer lines are split and
+may not be parsed correctly.


COMPILING AND INSTALLING:
Reply all
Reply to author
Forward
0 new messages