Bug 1851154 - Provide our own implementation of C's getline for Android r=jld

10 years ago (!) Bug 914190 already choked on the fact that bionic's
getline implementation could realloc a buffer using a function call
we cannot intercept, resulting in different memory allocator being used
to allocate and free the getline buffer.

This got hit again by 1850948, causing a backout. The approach taken at
that time (use std::getline) is neither future-proof (as demonstrated by
the backout) nor always satisfying (std::string as a few limitations in
term of low-level buffer manipulation).

Provide our implementation for Android, as hinted by the original bug.

Differential Revision: https://phabricator.services.mozilla.com/D187270
This commit is contained in:
serge-sans-paille 2023-09-04 07:00:31 +00:00
parent a57cd0807a
commit 769ddf6425
2 changed files with 111 additions and 0 deletions

View File

@ -0,0 +1,110 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* Interposing getline because of
* https://bugzilla.mozilla.org/show_bug.cgi?id=914190
*/
#ifdef __ANDROID__
# include <cstdlib>
# include <cstdio>
# include <cerrno>
# include <limits>
namespace {
// RAII on file locking.
class FileLocker {
FILE* stream;
public:
explicit FileLocker(FILE* stream) : stream(stream) { flockfile(stream); }
~FileLocker() { funlockfile(stream); }
};
ssize_t internal_getdelim(char** __restrict lineptr, size_t* __restrict n,
int delim, FILE* __restrict stream) {
constexpr size_t n_default = 64;
constexpr size_t n_max =
std::numeric_limits<ssize_t>::max() < std::numeric_limits<size_t>::max()
? (size_t)std::numeric_limits<ssize_t>::max() + 1
: std::numeric_limits<size_t>::max();
constexpr size_t n_limit = 2 * ((n_max - 1) / 3);
if (!lineptr || !n || !stream) {
errno = EINVAL;
return -1;
}
// Lock the file so that we can us unlocked getc in the inner loop.
FileLocker fl(stream);
if (!*lineptr || *n == 0) {
*n = n_default;
if (auto* new_lineptr = reinterpret_cast<char*>(realloc(*lineptr, *n))) {
*lineptr = new_lineptr;
} else {
errno = ENOMEM;
return -1;
}
}
ssize_t result;
size_t cur_len = 0;
while (true) {
// Retrieve an extra char.
int i = getc_unlocked(stream);
if (i == EOF) {
result = -1;
break;
}
// Eventually grow the buffer.
if (cur_len + 1 >= *n) {
size_t needed = *n >= n_limit ? n_max : 3 * *n / 2 + 1;
if (cur_len + 1 >= needed) {
errno = EOVERFLOW;
return -1;
}
if (auto* new_lineptr = (char*)realloc(*lineptr, needed)) {
*lineptr = new_lineptr;
} else {
errno = ENOMEM;
return -1;
}
*n = needed;
}
(*lineptr)[cur_len] = i;
cur_len++;
if (i == delim) break;
}
(*lineptr)[cur_len] = '\0';
return cur_len ? cur_len : result;
}
} // namespace
extern "C" {
MFBT_API ssize_t getline(char** __restrict lineptr, size_t* __restrict n,
FILE* __restrict stream) {
return internal_getdelim(lineptr, n, '\n', stream);
}
MFBT_API ssize_t getdelim(char** __restrict lineptr, size_t* __restrict n,
int delim, FILE* __restrict stream) {
return internal_getdelim(lineptr, n, delim, stream);
}
} // extern "C"
#endif

View File

@ -9,6 +9,7 @@ DEFINES["IMPL_MFBT"] = True
UNIFIED_SOURCES += [
"env_interposer.cpp",
"getline_interposer.cpp",
]
if CONFIG["MOZ_CRASHREPORTER"]: