Dear all, this patch seems to be a very special challenge:
The testcase has an UTF-8 byte order mark, which I do not see in the patch unless I use "od". If you don't see it, then please see the attachment in the PR for reference. We used a too short line buffer when reading the testcase, leading to valgrind invalid read/writes. The solution here is to extend the line buffer, taking care of line length limits. I do hope I got it right. Regtested on x86_64-pc-linux-gnu. OK for mainline? Thanks, Harald
From 8f7db1002c2a98a9195c56313b4501e73c89524b Mon Sep 17 00:00:00 2001 From: Harald Anlauf <[email protected]> Date: Tue, 3 Nov 2026 23:19:53 +0100 Subject: [PATCH] fortran: Fix treatment of UTF-8 byte order marks in source files [PR128343] When a gfortran source file contains a BOM, we ignore it, but only on the first line of a file. In that case the temporary line buffer needs to be extended to avoid valgrind errors. PR fortran/128343 gcc/fortran/ChangeLog: * scanner.cc (load_line): Check for optional UTF-8 BOM at beginning of line. Adjust temporary line buffer length and line truncation. (load_file): Use it when reading the very first line of a source file. gcc/testsuite/ChangeLog: * gfortran.dg/pr128343.f: New test. --- gcc/fortran/scanner.cc | 33 ++++++++++++++++++++++++---- gcc/testsuite/gfortran.dg/pr128343.f | 22 +++++++++++++++++++ 2 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 gcc/testsuite/gfortran.dg/pr128343.f diff --git a/gcc/fortran/scanner.cc b/gcc/fortran/scanner.cc index 9b77e559065..85b25f77da5 100644 --- a/gcc/fortran/scanner.cc +++ b/gcc/fortran/scanner.cc @@ -1801,6 +1801,9 @@ gfc_gobble_whitespace (void) the first character of the line, which has already been read by the caller. This avoids the use of ungetc(). + If allow_bom is true, increase length of line buffer to allow for UTF-8 BOM + (byte order mark) at the beginning of the line. + load_line returns whether the line was truncated. NOTE: The error machinery isn't available at this point, so we can't @@ -1808,7 +1811,8 @@ gfc_gobble_whitespace (void) parts of gfortran. */ static bool -load_line (FILE *input, gfc_char_t **pbuf, int *pbuflen, const int *first_char) +load_line (FILE *input, gfc_char_t **pbuf, int *pbuflen, const int *first_char, + bool allow_bom = false) { int c, maxlen, i, preprocessor_flag, buflen = *pbuflen; int quoted = ' ', comment_ix = -1; @@ -1819,6 +1823,7 @@ load_line (FILE *input, gfc_char_t **pbuf, int *pbuflen, const int *first_char) bool seen_ampersand = false; bool found_tab = false; bool warned_tabs = false; + bool seen_utf8_bom = false; gfc_char_t *buffer; /* Determine the maximum allowed line length. */ @@ -1841,6 +1846,10 @@ load_line (FILE *input, gfc_char_t **pbuf, int *pbuflen, const int *first_char) else buflen = 132; + /* Increase line buffer length for BOM. */ + if (allow_bom) + buflen += 3; + *pbuf = gfc_get_wide_string (buflen + 1); } @@ -1977,6 +1986,18 @@ load_line (FILE *input, gfc_char_t **pbuf, int *pbuflen, const int *first_char) *buffer++ = c; i++; + /* Check for possible UTF-8 BOM. */ + if (allow_bom + && i == 3 + && buffer[-3] == (unsigned char) '\xEF' + && buffer[-2] == (unsigned char) '\xBB' + && buffer[-1] == (unsigned char) '\xBF') + seen_utf8_bom = true; + + /* Check for sentinel '#' after BOM. */ + if (i == 4 && seen_utf8_bom) + preprocessor_flag = (c == '#'); + if (maxlen == 0 || preprocessor_flag) { if (i >= buflen) @@ -1988,7 +2009,7 @@ load_line (FILE *input, gfc_char_t **pbuf, int *pbuflen, const int *first_char) buffer = (*pbuf) + i; } } - else if (i >= maxlen) + else if (i >= maxlen + (seen_utf8_bom ? 3 : 0)) { bool trunc_warn = true; @@ -2035,7 +2056,7 @@ next_char: && !preprocessor_flag && c != EOF) { - while (i++ < maxlen) + while (i++ < maxlen + (seen_utf8_bom ? 3 : 0)) *buffer++ = ' '; } @@ -2541,6 +2562,7 @@ load_file (const char *realfilename, const char *displayedname, bool initial) FILE *input; int len, line_len; bool first_line; + bool allow_bom; struct stat st; int stat_result; const char *filename; @@ -2632,11 +2654,14 @@ load_file (const char *realfilename, const char *displayedname, bool initial) } } + /* Allow BOM on very first line of file. */ + allow_bom = true; for (;;) { - int trunc = load_line (input, &line, &line_len, NULL); + int trunc = load_line (input, &line, &line_len, NULL, allow_bom); int inc_line; + allow_bom = false; len = gfc_wide_strlen (line); if (feof (input) && len == 0) break; diff --git a/gcc/testsuite/gfortran.dg/pr128343.f b/gcc/testsuite/gfortran.dg/pr128343.f new file mode 100644 index 00000000000..e7767eb0897 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/pr128343.f @@ -0,0 +1,22 @@ +#define a(i) b(i) +! +! PR fortran/128343 - UTF-8 byte order mark and internal line buffer +! +! { dg-additional-options "-w" } +! +! Note: this file has the following characters at the beginning: +! +! 0000000 357 273 277 043 144 145 146 151 156 145 040 141 050 151 051 040 +! 357 273 277 # d e f i n e a ( i ) +! +! which lead to invalid write/read errors under valgrind. +! +! Derived from 1066_0007.f from the Fujitsu test suite on Github. + +#if 0 +#else ! a + ! a + write(10,*) a + print *,'pass' +#endif ! a a + end -- 2.51.0
