From 916124ed841745b7a1e0fbc43f9909340b47d373 Mon Sep 17 00:00:00 2001 From: Florian Weimer Date: Fri, 6 Jul 2018 14:23:15 +0200 Subject: nss_files: Fix re-reading of long lines [BZ #18991] Use the new __libc_readline_unlocked function to pick up reading at the same line in case the buffer needs to be enlarged. --- nss/nss_files/files-XXX.c | 121 +++++++++++++++++++--------------------------- 1 file changed, 50 insertions(+), 71 deletions(-) (limited to 'nss/nss_files/files-XXX.c') diff --git a/nss/nss_files/files-XXX.c b/nss/nss_files/files-XXX.c index df33551258..60d1544a96 100644 --- a/nss/nss_files/files-XXX.c +++ b/nss/nss_files/files-XXX.c @@ -128,51 +128,6 @@ CONCAT(_nss_files_end,ENTNAME) (void) } -typedef enum -{ - gcr_ok = 0, - gcr_error = -1, - gcr_overflow = -2 -} get_contents_ret; - -/* Hack around the fact that fgets only accepts int sizes. */ -static get_contents_ret -get_contents (char *linebuf, size_t len, FILE *stream) -{ - size_t remaining_len = len; - char *curbuf = linebuf; - - do - { - int curlen = ((remaining_len > (size_t) INT_MAX) ? INT_MAX - : remaining_len); - - /* Terminate the line so that we can test for overflow. */ - ((unsigned char *) curbuf)[curlen - 1] = 0xff; - - char *p = fgets_unlocked (curbuf, curlen, stream); - - /* EOF or read error. */ - if (p == NULL) - return gcr_error; - - /* Done reading in the line. */ - if (((unsigned char *) curbuf)[curlen - 1] == 0xff) - return gcr_ok; - - /* Drop the terminating '\0'. */ - remaining_len -= curlen - 1; - curbuf += curlen - 1; - } - /* fgets copies one less than the input length. Our last iteration is of - REMAINING_LEN and once that is done, REMAINING_LEN is decremented by - REMAINING_LEN - 1, leaving the result as 1. */ - while (remaining_len > 1); - - /* This means that the current buffer was not large enough. */ - return gcr_overflow; -} - /* Parsing the database file into `struct STRUCTURE' data structures. */ static enum nss_status internal_getent (FILE *stream, struct STRUCTURE *result, @@ -191,45 +146,69 @@ internal_getent (FILE *stream, struct STRUCTURE *result, return NSS_STATUS_TRYAGAIN; } - do + while (true) { - get_contents_ret r = get_contents (data->linebuffer, linebuflen, stream); - - if (r == gcr_error) + ssize_t r = __libc_readline_unlocked + (stream, data->linebuffer, linebuflen); + if (r < 0) + { + *errnop = errno; + H_ERRNO_SET (NETDB_INTERNAL); + if (*errnop == ERANGE) + /* Request larger buffer. */ + return NSS_STATUS_TRYAGAIN; + else + /* Other read failure. */ + return NSS_STATUS_UNAVAIL; + } + else if (r == 0) { - /* End of file or read error. */ + /* End of file. */ H_ERRNO_SET (HOST_NOT_FOUND); return NSS_STATUS_NOTFOUND; } - if (r == gcr_overflow) + /* Everything OK. Now skip leading blanks. */ + p = data->linebuffer; + while (isspace (*p)) + ++p; + + /* Ignore empty and comment lines. */ + if (*p == '\0' || *p == '#') + continue; + + /* Parse the line. */ + *errnop = EINVAL; + parse_result = parse_line (p, result, data, buflen, errnop EXTRA_ARGS); + + if (parse_result == -1) { - /* The line is too long. Give the user the opportunity to - enlarge the buffer. */ - *errnop = ERANGE; + if (*errnop == ERANGE) + { + /* Return to the original file position at the beginning + of the line, so that the next call can read it again + if necessary. */ + if (__fseeko64 (stream, -r, SEEK_CUR) != 0) + { + if (errno == ERANGE) + *errnop = EINVAL; + else + *errnop = errno; + H_ERRNO_SET (NETDB_INTERNAL); + return NSS_STATUS_UNAVAIL; + } + } H_ERRNO_SET (NETDB_INTERNAL); return NSS_STATUS_TRYAGAIN; } - /* Everything OK. Now skip leading blanks. */ - p = data->linebuffer; - while (isspace (*p)) - ++p; - } - while (*p == '\0' || *p == '#' /* Ignore empty and comment lines. */ - /* Parse the line. If it is invalid, loop to get the next - line of the file to parse. */ - || ! (parse_result = parse_line (p, result, data, buflen, errnop - EXTRA_ARGS))); + /* Return the data if parsed successfully. */ + if (parse_result != 0) + return NSS_STATUS_SUCCESS; - if (__glibc_unlikely (parse_result == -1)) - { - H_ERRNO_SET (NETDB_INTERNAL); - return NSS_STATUS_TRYAGAIN; + /* If it is invalid, loop to get the next line of the file to + parse. */ } - - /* Filled in RESULT with the next entry from the database file. */ - return NSS_STATUS_SUCCESS; } -- cgit 1.4.1