preprocessor/58580 - preprocessor goes OOM with warning for zero literals
In this problem report, the compiler is fed a (bogus) translation unit in which some literals contain bytes whose value is zero. The preprocessor detects that and proceeds to emit diagnostics for that king of bogus literals. But then when the diagnostics machinery re-reads the input file again to display the bogus literals with a caret, it attempts to calculate the length of each of the lines it got using fgets. The line length calculation is done using strlen. But that doesn't work well when the content of the line can have several zero bytes. The result is that the read_line never sees the end of the line because strlen repeatedly reports that the line ends before the end-of-line character; so read_line thinks its buffer for reading the line is too small; it thus increases the buffer, leading to a huge memory consumption, pain and disaster. The patch below introduces a new get_line function that returns the next line of a file and return the length of that line even if the line contains zero bytes. That get_line function has been adapted from the getline function from the GNU C Library because getline being a GNU extension it is not necessarily supported on all platforms. read_line is then modified to return the length of the line along with the line itself, as the line can now contain zero bytes. Callers of read_line are adjusted consequently. diagnostic_show_locus() is modified to consider that a line can have characters of value zero, and so just shows a white space when instructed to display one of these characters. gcc/ChangeLog: * input.h (location_get_source_line): Take an additional line_size parameter. * input.c (get_line): New static function definition. (read_line): Take an additional line_length output parameter to be set to the size of the line. Use the new get_line function do the actual line reading. (location_get_source_line): Take an additional output line_len parameter. Update the use of read_line to pass it the line_len parameter. * diagnostic.c (adjust_line): Take an additional input parameter for the length of the line, rather than calculating it with strlen. (diagnostic_show_locus): Adjust the use of location_get_source_line and adjust_line with respect to their new signature. While displaying a line now, do not stop at the first null byte. Rather, display the zero byte as a space and keep going until we reach the size of the line. gcc/testsuite/ChangeLog: * c-c++-common/cpp/warning-zero-in-literals-1.c: New test file. From-SVN: r204453
This commit is contained in:
parent
6dce150ccf
commit
9789a91276
6 changed files with 131 additions and 38 deletions
|
@ -1,3 +1,24 @@
|
|||
2013-11-06 Dodji Seketeli <dodji@redhat.com>
|
||||
|
||||
preprocessor/58580
|
||||
* input.h (location_get_source_line): Take an additional line_size
|
||||
parameter.
|
||||
* input.c (get_line): New static function definition.
|
||||
(read_line): Take an additional line_length output parameter to be
|
||||
set to the size of the line. Use the new get_line function do the
|
||||
actual line reading.
|
||||
(location_get_source_line): Take an additional output line_len
|
||||
parameter. Update the use of read_line to pass it the line_len
|
||||
parameter.
|
||||
* diagnostic.c (adjust_line): Take an additional input parameter
|
||||
for the length of the line, rather than calculating it with
|
||||
strlen.
|
||||
(diagnostic_show_locus): Adjust the use of
|
||||
location_get_source_line and adjust_line with respect to their new
|
||||
signature. While displaying a line now, do not stop at the first
|
||||
null byte. Rather, display the zero byte as a space and keep
|
||||
going until we reach the size of the line.
|
||||
|
||||
2013-11-06 Eric Botcazou <ebotcazou@adacore.com>
|
||||
|
||||
* config/i386/i386.c (ix86_expand_prologue): Optimize stack checking for
|
||||
|
|
|
@ -259,12 +259,13 @@ diagnostic_build_prefix (diagnostic_context *context,
|
|||
MAX_WIDTH by some margin, then adjust the start of the line such
|
||||
that the COLUMN is smaller than MAX_WIDTH minus the margin. The
|
||||
margin is either 10 characters or the difference between the column
|
||||
and the length of the line, whatever is smaller. */
|
||||
and the length of the line, whatever is smaller. The length of
|
||||
LINE is given by LINE_WIDTH. */
|
||||
static const char *
|
||||
adjust_line (const char *line, int max_width, int *column_p)
|
||||
adjust_line (const char *line, int line_width,
|
||||
int max_width, int *column_p)
|
||||
{
|
||||
int right_margin = 10;
|
||||
int line_width = strlen (line);
|
||||
int column = *column_p;
|
||||
|
||||
right_margin = MIN (line_width - column, right_margin);
|
||||
|
@ -284,6 +285,7 @@ diagnostic_show_locus (diagnostic_context * context,
|
|||
const diagnostic_info *diagnostic)
|
||||
{
|
||||
const char *line;
|
||||
int line_width;
|
||||
char *buffer;
|
||||
expanded_location s;
|
||||
int max_width;
|
||||
|
@ -297,22 +299,25 @@ diagnostic_show_locus (diagnostic_context * context,
|
|||
|
||||
context->last_location = diagnostic->location;
|
||||
s = expand_location_to_spelling_point (diagnostic->location);
|
||||
line = location_get_source_line (s);
|
||||
line = location_get_source_line (s, &line_width);
|
||||
if (line == NULL)
|
||||
return;
|
||||
|
||||
max_width = context->caret_max_width;
|
||||
line = adjust_line (line, max_width, &(s.column));
|
||||
line = adjust_line (line, line_width, max_width, &(s.column));
|
||||
|
||||
pp_newline (context->printer);
|
||||
saved_prefix = pp_get_prefix (context->printer);
|
||||
pp_set_prefix (context->printer, NULL);
|
||||
pp_space (context->printer);
|
||||
while (max_width > 0 && *line != '\0')
|
||||
while (max_width > 0 && line_width > 0)
|
||||
{
|
||||
char c = *line == '\t' ? ' ' : *line;
|
||||
if (c == '\0')
|
||||
c = ' ';
|
||||
pp_character (context->printer, c);
|
||||
max_width--;
|
||||
line_width--;
|
||||
line++;
|
||||
}
|
||||
pp_newline (context->printer);
|
||||
|
|
123
gcc/input.c
123
gcc/input.c
|
@ -87,53 +87,114 @@ expand_location_1 (source_location loc,
|
|||
return xloc;
|
||||
}
|
||||
|
||||
/* Reads one line from file into a static buffer. */
|
||||
/* This function reads a line that might contain bytes whose value is
|
||||
zero. It returns the number of bytes read. The 'end-of-line'
|
||||
character found at the end of the line is not contained in the
|
||||
returned buffer. Note that this function has been adapted from
|
||||
getline() and _IO_getdelim() GNU C library functions. It's been
|
||||
duplicated here because the getline() function is not necessarily
|
||||
present on all platforms.
|
||||
|
||||
LINEPTR points to a buffer that is to contain the line read.
|
||||
|
||||
N points to the size of the the LINEPTR buffer.
|
||||
|
||||
FP points to the file to consider. */
|
||||
|
||||
static ssize_t
|
||||
get_line (char **lineptr, size_t *n, FILE *fp)
|
||||
{
|
||||
ssize_t cur_len = 0, len;
|
||||
char buf[16384];
|
||||
|
||||
if (lineptr == NULL || n == NULL)
|
||||
return -1;
|
||||
|
||||
if (*lineptr == NULL || *n == 0)
|
||||
{
|
||||
*n = 120;
|
||||
*lineptr = XNEWVEC (char, *n);
|
||||
}
|
||||
|
||||
len = fread (buf, 1, sizeof buf, fp);
|
||||
if (ferror (fp))
|
||||
return -1;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
size_t needed;
|
||||
char *t = (char*) memchr (buf, '\n', len);
|
||||
if (t != NULL) len = (t - buf);
|
||||
if (__builtin_expect (len >= SSIZE_MAX - cur_len, 0))
|
||||
return -1;
|
||||
needed = cur_len + len + 1;
|
||||
if (needed > *n)
|
||||
{
|
||||
char *new_lineptr;
|
||||
if (needed < 2 * *n)
|
||||
needed = 2 * *n;
|
||||
new_lineptr = XRESIZEVEC (char, *lineptr, needed);
|
||||
*lineptr = new_lineptr;
|
||||
*n = needed;
|
||||
}
|
||||
memcpy (*lineptr + cur_len, buf, len);
|
||||
cur_len += len;
|
||||
if (t != NULL)
|
||||
break;
|
||||
len = fread (buf, 1, sizeof buf, fp);
|
||||
if (ferror (fp))
|
||||
return -1;
|
||||
if (len == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (cur_len)
|
||||
(*lineptr)[cur_len] = '\0';
|
||||
return cur_len;
|
||||
}
|
||||
|
||||
/* Reads one line from FILE into a static buffer. If LINE_LENGTH is
|
||||
*non-null LINE_LENGTH, will be set by this function to the length of
|
||||
*the returned line. Note that the returned line can contain several
|
||||
*zero bytes. Also note that the returned string is allocated in
|
||||
*static storage that is going to be re-used by subsequent invocations
|
||||
*of read_line. */
|
||||
static const char *
|
||||
read_line (FILE *file)
|
||||
read_line (FILE *file, int *line_length)
|
||||
{
|
||||
static char *string;
|
||||
static size_t string_len;
|
||||
size_t pos = 0;
|
||||
char *ptr;
|
||||
int len;
|
||||
|
||||
if (!string_len)
|
||||
{
|
||||
string_len = 200;
|
||||
string = XNEWVEC (char, string_len);
|
||||
}
|
||||
|
||||
while ((ptr = fgets (string + pos, string_len - pos, file)))
|
||||
{
|
||||
size_t len = strlen (string + pos);
|
||||
|
||||
if (string[pos + len - 1] == '\n')
|
||||
{
|
||||
string[pos + len - 1] = 0;
|
||||
return string;
|
||||
}
|
||||
pos += len;
|
||||
string = XRESIZEVEC (char, string, string_len * 2);
|
||||
string_len *= 2;
|
||||
}
|
||||
|
||||
return pos ? string : NULL;
|
||||
len = get_line (&string, &string_len, file);
|
||||
if (line_length)
|
||||
*line_length = len;
|
||||
return len ? string : NULL;
|
||||
}
|
||||
|
||||
/* Return the physical source line that corresponds to xloc in a
|
||||
buffer that is statically allocated. The newline is replaced by
|
||||
the null character. */
|
||||
the null character. Note that the line can contain several null
|
||||
characters, so LINE_LEN, if non-null, points to the actual length
|
||||
of the line. */
|
||||
|
||||
const char *
|
||||
location_get_source_line (expanded_location xloc)
|
||||
location_get_source_line (expanded_location xloc,
|
||||
int *line_len)
|
||||
{
|
||||
const char *buffer;
|
||||
int lines = 1;
|
||||
const char *buffer = NULL, *ptr;
|
||||
int lines = 0, len = 0;
|
||||
FILE *stream = xloc.file ? fopen (xloc.file, "r") : NULL;
|
||||
if (!stream)
|
||||
return NULL;
|
||||
|
||||
while ((buffer = read_line (stream)) && lines < xloc.line)
|
||||
lines++;
|
||||
while ((ptr = read_line (stream, &len)) && lines < xloc.line)
|
||||
{
|
||||
buffer = ptr;
|
||||
lines++;
|
||||
if (line_len)
|
||||
*line_len = len;
|
||||
}
|
||||
|
||||
fclose (stream);
|
||||
return buffer;
|
||||
|
|
|
@ -37,7 +37,8 @@ extern char builtins_location_check[(BUILTINS_LOCATION
|
|||
< RESERVED_LOCATION_COUNT) ? 1 : -1];
|
||||
|
||||
extern expanded_location expand_location (source_location);
|
||||
extern const char *location_get_source_line (expanded_location xloc);
|
||||
extern const char *location_get_source_line (expanded_location xloc,
|
||||
int *line_size);
|
||||
extern expanded_location expand_location_to_spelling_point (source_location);
|
||||
extern source_location expansion_point_location_if_in_system_header (source_location);
|
||||
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
2013-11-06 Dodji Seketeli <dodji@redhat.com>
|
||||
|
||||
preprocessor/58580
|
||||
* c-c++-common/cpp/warning-zero-in-literals-1.c: New test file.
|
||||
|
||||
2013-11-06 Christian Bruel <christian.bruel@st.com>
|
||||
|
||||
* gcc.target/sh/strlen.c: New test.
|
||||
|
|
BIN
gcc/testsuite/c-c++-common/cpp/warning-zero-in-literals-1.c
Normal file
BIN
gcc/testsuite/c-c++-common/cpp/warning-zero-in-literals-1.c
Normal file
Binary file not shown.
Loading…
Add table
Reference in a new issue