vsprintf into 1024-byte runtime_err_str overflows on long file name in the FILE ASSIGN error pathA plain COBOL program that OPENs a file with a name longer than ~1010 bytes — no FILE STATUS declared — triggers cob_fatal_error → cob_runtime_error, which formats the file name verbatim into the fixed 1024-byte global runtime_err_str via unbounded vsprintf (libcob/common.c:9058). The overflow happens entirely inside the library's error path; the same call is also reachable via cob_load_collation() (cconv.c:170) and cob_check_numeric().
$ git describe --tags --always
a672a26b
origin/gnucobol-3.x tip)a672a26b52b594bd0ebfdcfe0200613c572018d5 (2026-06-09)cob_setup_runtime_error_str() formats the message into runtime_err_str[1024] with unbounded vsprintf:
// libcob/common.c:9044-9059
static void COB_NOINLINE
cob_setup_runtime_error_str (const char *fmt, va_list ap)
{
char *p = runtime_err_str;
/* ... optional "file:line: " prefix via sprintf, advancing p ... */
vsprintf (p, fmt, ap); // line 9058 — should be vsnprintf(p, runtime_err_str + COB_ERRBUF_SIZE - p, fmt, ap)
}
Any error message over ~1010 bytes overflows. No public API is needed: OPEN INPUT F on a missing file with no FILE STATUS clause escalates to cob_fatal_error → cob_runtime_error("%s (status = %02d) for file %s", msg, status, err_cause) with the full caller-supplied file name. A file name in SELECT ... ASSIGN TO is just a program data item, and omitting FILE STATUS is legal COBOL, so the fault is in the library's error path, not the program.
IDENTIFICATION DIVISION.
PROGRAM-ID. poc-file.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT F ASSIGN TO WS-FNAME.
DATA DIVISION.
FILE SECTION.
FD F.
01 R PIC X(10).
WORKING-STORAGE SECTION.
01 WS-FNAME PIC X(1500) VALUE ALL 'A'.
PROCEDURE DIVISION.
OPEN INPUT F.
DISPLAY "opened" UPON SYSOUT.
STOP RUN.
==309645==ERROR: AddressSanitizer: global-buffer-overflow
WRITE of size 1551 at 0x70f12d08ffc0 thread T0
#2 cob_setup_runtime_error_str libcob/common.c:9058
#3 cob_runtime_error libcob/common.c:9071
#4 cob_fatal_error libcob/common.c:9341
#5 poc__file_ (poc_min)
0x70f12d08ffc0 is located 0 bytes after global variable 'runtime_err_str' ... of size 1024
SUMMARY: AddressSanitizer: global-buffer-overflow ... in __vsprintf_chk
# One-shot Docker reproduction (see gnucobol-issue-attachments.zip):
unzip gnucobol-issue-attachments.zip && cd gnucobol-issue-attachments
./run.sh
# => AddressSanitizer: global-buffer-overflow ... WRITE of size 1551
The same issue is also reproducible on some online COBOL compilers.
This report was produced by FuzzAnything's AI-assisted library fuzzer and manually verified by a team member.
Signed-off-by: FuzzAnything fuzzanything@gmail.com
Would it be appropriate to truncate the displayed filename to 80 characters (or some similar limit) if the filename is longer than the limit?
Would that be a difficult or expensive change for the maintainers?
We already do that at other places (search the code for" ..." to see how/where that is used), and YES, it is reasonable to memcpy the filename into a local buffer of appropriate size (whatever we use in the other places), if it is too big, replace the last 4 chars (including NUL) by "....", and then sprintf from there, not the original name, before calling into this function in the "for file %s" case.
Furthermore we should use vsnprintf as noted in the bug report.
Patches welcome.
We need a general fix for the cob_runtime_error path which fixes the SIGSEGV - outlined in [bugs:#1247] - and a specific "trim down path" + testcase (run_file.at) as outlined here.
Note that this bug is actually "more relevant" than the other issue, as this can be triggered from a "normal" COBOL program in a relative "normal" path (file just not exists where expected).
Related
Bugs: #1247