From 498287b3c340739bede9a3fa95ea23b6ae650bf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20F=C3=A6r=C3=B8y?= Date: Mon, 27 Feb 2017 15:37:31 +0100 Subject: [PATCH] Add compatibility function for fgets(3). This patch adds the `tor_fgets()` function to our compatibility layer. `tor_fgets()` adds an additional check for whether the error-bit have been enabled for the given file stream, if that is the case and `errno` is set to `EAGAIN` we make sure that we always return NULL. Unfortunately `fgets(3)` behaves differently on different versions of the C library. See: https://bugs.torproject.org/21416 See: https://bugs.torproject.org/20988 --- changes/bug20988 | 4 ++++ src/common/compat.c | 39 +++++++++++++++++++++++++++++++++++++++ src/common/compat.h | 2 ++ 3 files changed, 45 insertions(+) create mode 100644 changes/bug20988 diff --git a/changes/bug20988 b/changes/bug20988 new file mode 100644 index 0000000000..b1d73f08e7 --- /dev/null +++ b/changes/bug20988 @@ -0,0 +1,4 @@ + o Minor bugfixes (portability): + - Add Tor compatibility function for fgets(3) due to inconsistency of + returned values in different supported C libraries. This fixes unit test + failures reported on FreeBSD. Fixes bug 20988. diff --git a/src/common/compat.c b/src/common/compat.c index 0dbede6bed..da4283fbaa 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -3476,6 +3476,45 @@ tor_getpass(const char *prompt, char *output, size_t buflen) #endif } +/** Read at most one less than the number of characters specified by + * size from the given stream and store it in str. + * + * Reading stops when a newline character is found or at EOF or error. If any + * characters are read and there's no error, a trailing NUL byte is appended to + * the end of str. + * + * Upon successful completion, this function returns a pointer to the string + * str. If EOF occurs before any characters are read the function will + * return NULL and the content of str is unchanged. Upon error, the + * function returns NULL and the caller must check for error using foef(3) and + * ferror(3). + */ +char * +tor_fgets(char *str, int size, FILE *stream) +{ + char *ret; + + /* Reset errno before our call to fgets(3) to avoid a situation where the + * caller is calling us again because we just returned NULL and errno == + * EAGAIN, but when they call us again we will always return NULL because the + * error flag on the file handler remains set and errno is set to EAGAIN. + */ + errno = 0; + + ret = fgets(str, size, stream); + + /* FreeBSD, OpenBSD, Linux (glibc), and Linux (musl) seem to disagree about + * what to do in the given situation. We check if the stream has been flagged + * with an error-bit and return NULL in that situation if errno is also set + * to EAGAIN. + */ + if (ferror(stream) && errno == EAGAIN) { + return NULL; + } + + return ret; +} + /** Return the amount of free disk space we have permission to use, in * bytes. Return -1 if the amount of free space can't be determined. */ int64_t diff --git a/src/common/compat.h b/src/common/compat.h index ee1c9454de..1f51ece61f 100644 --- a/src/common/compat.h +++ b/src/common/compat.h @@ -740,6 +740,8 @@ STATIC int tor_ersatz_socketpair(int family, int type, int protocol, ssize_t tor_getpass(const char *prompt, char *output, size_t buflen); +char *tor_fgets(char *str, int size, FILE *stream); + /* This needs some of the declarations above so we include it here. */ #include "compat_threads.h"