Browse Source

[core] handle unexpected EOF reading FILE_CHUNK

(replace existing check which suffered from ToC-ToU race condition)
enhances logic from 2015 commit 593599f1 and avoids repeated fstat()
checks when sending large files

For mmap(), lighttpd catches SIGBUS if file is (externally) truncated
and lighttpd attempts to access bytes in a read-only mapping more than
a memory page boundary following the end of the file.

For sendfile(), lighttpd returns an error if sendfile() reports no error
and that no bytes have been sent after lighttpd attempts to send a
non-zero number of bytes.
master
Glenn Strauss 9 months ago
parent
commit
7c1e81299f
  1. 4
      src/chunk.c
  2. 17
      src/network_write.c

4
src/chunk.c

@ -897,7 +897,7 @@ chunkqueue_peek_data (chunkqueue * const cq,
}
case FILE_CHUNK:
if (0 == chunk_open_file_chunk(c, errh)) {
if (c->file.fd >= 0 || 0 == chunk_open_file_chunk(c, errh)) {
off_t offset = c->file.start + c->offset;
off_t toSend = c->file.length - c->offset;
if (toSend > (off_t)space)
@ -908,7 +908,7 @@ chunkqueue_peek_data (chunkqueue * const cq,
return -1;
}
toSend = read(c->file.fd, data_in + *dlen, (size_t)toSend);
if (-1 == toSend) {
if (toSend <= 0) { /* -1 error; 0 EOF (unexpected) */
log_perror(errh, __FILE__, __LINE__, "read");
return -1;
}

17
src/network_write.c

@ -157,7 +157,7 @@ static int network_write_file_chunk_no_mmap(int fd, chunkqueue *cq, off_t *p_max
return 0;
}
if (0 != chunkqueue_open_file_chunk(cq, errh)) return -1;
if (c->file.fd < 0 && 0 != chunkqueue_open_file_chunk(cq, errh)) return -1;
if (toSend > (off_t)sizeof(buf)) toSend = (off_t)sizeof(buf);
@ -165,8 +165,8 @@ static int network_write_file_chunk_no_mmap(int fd, chunkqueue *cq, off_t *p_max
log_perror(errh, __FILE__, __LINE__, "lseek");
return -1;
}
if (-1 == (toSend = read(c->file.fd, buf, toSend))) {
log_perror(errh, __FILE__, __LINE__, "read");
if ((toSend = read(c->file.fd, buf, toSend)) <= 0) {
log_perror(errh, __FILE__, __LINE__, "read");/* err or unexpected EOF */
return -1;
}
@ -233,7 +233,7 @@ static int network_write_file_chunk_mmap(int fd, chunkqueue *cq, off_t *p_max_by
return 0;
}
if (0 != chunkqueue_open_file_chunk(cq, errh)) return -1;
if (c->file.fd < 0 && 0 != chunkqueue_open_file_chunk(cq, errh)) return -1;
/* mmap buffer if offset is outside old mmap area or not mapped at all */
if (MAP_FAILED == c->file.mmap.start
@ -452,7 +452,7 @@ static int network_write_file_chunk_sendfile(int fd, chunkqueue *cq, off_t *p_ma
return 0;
}
if (0 != chunkqueue_open_file_chunk(cq, errh)) return -1;
if (c->file.fd < 0 && 0 != chunkqueue_open_file_chunk(cq, errh)) return -1;
/* Darwin, FreeBSD, and Solaris variants support iovecs and could
* be optimized to send more than just file in single syscall */
@ -526,10 +526,15 @@ static int network_write_file_chunk_sendfile(int fd, chunkqueue *cq, off_t *p_ma
}
}
if (written >= 0) { /*(always true)*/
if (written > 0) {
chunkqueue_mark_written(cq, written);
*p_max_bytes -= written;
}
else if (0 == wr) { /*(-1 != wr && 0 == written)*/
log_error(errh, __FILE__, __LINE__,
"sendfile(): fd: %d file truncated", fd);
return -1;
}
return (wr >= 0 && written == toSend) ? 0 : -3;
}

Loading…
Cancel
Save