Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 30 additions & 8 deletions lib/fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ uc_fs_read_common(uc_vm_t *vm, size_t nargs, FILE **fp)
const char *lstr;
int64_t lsize;
ssize_t llen;
int err;

if (!fp || !*fp)
err_return(EBADF);
Expand All @@ -216,23 +217,44 @@ uc_fs_read_common(uc_vm_t *vm, size_t nargs, FILE **fp)
len = (size_t)llen;
}
else if (llen == 3 && !strcmp(lstr, "all")) {
clearerr(*fp);

while (true) {
errno = 0;
rlen = fread(buf, 1, sizeof(buf), *fp);
err = errno;

if (rlen > 0) {
tmp = realloc(p, len + rlen);

tmp = realloc(p, len + rlen);
if (!tmp) {
free(p);
err_return(ENOMEM);
}

if (!tmp) {
free(p);
err_return(ENOMEM);
memcpy(tmp + len, buf, rlen);

p = tmp;
len += rlen;
}

memcpy(tmp + len, buf, rlen);
if (rlen == sizeof(buf))
continue;

p = tmp;
len += rlen;
/* no error means EOF */
if (!ferror(*fp))
break;

if (rlen == 0)
clearerr(*fp);

if (err == EINTR)
continue;

if (err == EAGAIN || err == EWOULDBLOCK)
break;

free(p);
err_return(err ? err : EIO);
}
}
else if (llen == 1) {
Expand Down
33 changes: 33 additions & 0 deletions tests/custom/06_lib_uloop/14_read_all_eintr
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
read("all") retries a read that a signal interrupted instead of treating it
as end of file, whether or not data arrived before the interruption.

libubox installs its SIGCHLD handler without SA_RESTART, so in a uloop based
host (rpcd, uhttpd, ...) any child exiting while the VM is blocked in a read
interrupts that read with EINTR.

-- Testcase --
{%
import * as uloop from 'uloop';
import { popen, error } from 'fs';

uloop.init();

// An unrelated child that exits while we are blocked reading
let other = popen('sleep 1', 'r');

let proc = popen('sleep 2; echo payload', 'r');

let data = proc.read('all');

printf('data: %J\nerror: %J\nexit: %d\n', data, error(), proc.close());

other.close();
uloop.done();
%}
-- End --

-- Expect stdout --
data: "payload\n"
error: null
exit: 0
-- End --
17 changes: 17 additions & 0 deletions tests/custom/99_bugs/64_fs_read_all_error
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
read("all") reports a read error as null plus fs.error() instead of
returning an empty string as if it had reached end of file.

-- Testcase --
{%
import { open, error } from 'fs';

let fp = open('/', 'r');

printf('data: %J\nerror: %J\n', fp.read('all'), error());
%}
-- End --

-- Expect stdout --
data: null
error: "Is a directory"
-- End --
Loading