Avoid pathological behavior in OpenBSD's fread.

(cherry picked from commit 20841a137b)

Bug: https://code.google.com/p/android/issues/detail?id=81155
Bug: 18556607
Change-Id: Ibdfebc20dce4c34ad565014523c9b074e90ea665
This commit is contained in:
Elliott Hughes 2014-12-01 16:13:30 -08:00
parent 152e978f73
commit 27d276f3a6
3 changed files with 49 additions and 2 deletions

View file

@ -61,6 +61,7 @@ libc_common_src_files := \
bionic/sigsetmask.c \
bionic/system_properties_compat.c \
stdio/findfp.c \
stdio/fread.c \
stdio/snprintf.c\
stdio/sprintf.c \
@ -396,7 +397,6 @@ libc_upstream_openbsd_src_files := \
upstream-openbsd/lib/libc/stdio/fputs.c \
upstream-openbsd/lib/libc/stdio/fputwc.c \
upstream-openbsd/lib/libc/stdio/fputws.c \
upstream-openbsd/lib/libc/stdio/fread.c \
upstream-openbsd/lib/libc/stdio/freopen.c \
upstream-openbsd/lib/libc/stdio/fscanf.c \
upstream-openbsd/lib/libc/stdio/fseek.c \

View file

@ -68,7 +68,23 @@ fread(void *buf, size_t size, size_t count, FILE *fp)
fp->_r = 0;
total = resid;
p = buf;
while (resid > (r = fp->_r)) {
// BEGIN android-added
// Avoid pathological behavior on unbuffered files. OpenBSD
// will loop reading one byte then memcpying one byte!
if ((fp->_flags & __SNBF) != 0) {
// We know if we're unbuffered that our buffer is empty, so
// we can just read directly.
while (resid > 0 && (r = (*fp->_read)(fp->_cookie, p, resid)) > 0) {
p += r;
resid -= r;
}
FUNLOCKFILE(fp);
return ((total - resid) / size);
}
// END android-added
while (resid > (size_t)(r = fp->_r)) {
(void)memcpy((void *)p, (void *)fp->_p, (size_t)r);
fp->_p += r;
/* fp->_r = 0 ... done in __srefill */

View file

@ -694,3 +694,34 @@ TEST(stdio, fpos_t_and_seek) {
fclose(fp);
}
// https://code.google.com/p/android/issues/detail?id=81155
// http://b/18556607
TEST(stdio, fread_unbuffered_pathological_performance) {
FILE* fp = fopen("/dev/zero", "r");
ASSERT_TRUE(fp != NULL);
// Make this stream unbuffered.
setvbuf(fp, 0, _IONBF, 0);
char buf[65*1024];
memset(buf, 0xff, sizeof(buf));
time_t t0 = time(NULL);
for (size_t i = 0; i < 1024; ++i) {
fread(buf, 64*1024, 1, fp);
}
time_t t1 = time(NULL);
fclose(fp);
// 1024 64KiB reads should have been very quick.
ASSERT_LE(t1 - t0, 1);
for (size_t i = 0; i < 64*1024; ++i) {
ASSERT_EQ('\0', buf[i]);
}
for (size_t i = 64*1024; i < 65*1024; ++i) {
ASSERT_EQ('\xff', buf[i]);
}
}