Offer COREDUMP_SPARSE in coredump_req->mask now that there is something behind it. Refuse it without COREDUMP_HEADER. A zero frame cannot exist outside a framed stream.
A trailing hole is flushed by coredump_write() the way it always was. So its last byte goes out as a one-byte data frame and the frames cover the whole coredump. Signed-off-by: Christian Brauner (Amutable) <[email protected]> --- fs/coredump.c | 34 +++++++++++++++++++++- .../selftests/coredump/coredump_test_helpers.c | 3 +- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/fs/coredump.c b/fs/coredump.c index 364c89c5f82a..bdf2eae948d0 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -799,7 +799,7 @@ static bool coredump_sock_request(struct core_name *cn, struct coredump_params * .size = sizeof(struct coredump_req), .mask = COREDUMP_KERNEL | COREDUMP_USERSPACE | COREDUMP_REJECT | COREDUMP_WAIT | - COREDUMP_HEADER, + COREDUMP_HEADER | COREDUMP_SPARSE, .size_ack = sizeof(struct coredump_ack), }; struct coredump_ack ack = {}; @@ -854,6 +854,12 @@ static bool coredump_sock_request(struct core_name *cn, struct coredump_params * return false; } + /* Zero frames only exist inside a framed stream. */ + if ((ack.mask & COREDUMP_SPARSE) && !(ack.mask & COREDUMP_HEADER)) { + coredump_sock_mark(cprm->file, COREDUMP_MARK_CONFLICTING); + return false; + } + if (ack.spare) { coredump_sock_mark(cprm->file, COREDUMP_MARK_UNSUPPORTED); return false; @@ -1229,6 +1235,11 @@ static bool dump_framed(const struct coredump_params *cprm) return cprm->mask & COREDUMP_HEADER; } +static bool dump_sparse(const struct coredump_params *cprm) +{ + return cprm->mask & COREDUMP_SPARSE; +} + /* Describe the next @len bytes of the coredump. Returns the header size. */ static size_t dump_frame_init(struct coredump_params *cprm, enum coredump_frame_type type, u64 len) @@ -1292,11 +1303,32 @@ static int __dump_emit(struct coredump_params *cprm, const void *addr, int nr) return dump_write_iter(cprm, &iter, nr); } +/* Hand the server the length of the hole instead of the hole itself. */ +static int dump_skip_frame(struct coredump_params *cprm, size_t nr) +{ + struct kvec kvec; + struct iov_iter iter; + size_t hdr; + + if (dump_interrupted()) + return 0; + + hdr = dump_frame_init(cprm, COREDUMP_FRAME_ZERO, nr); + kvec.iov_base = cprm->frame; + kvec.iov_len = hdr; + iov_iter_kvec(&iter, ITER_SOURCE, &kvec, 1, hdr); + + return dump_write_iter(cprm, &iter, nr); +} + static int __dump_skip(struct coredump_params *cprm, size_t nr) { static char zeroes[PAGE_SIZE]; struct file *file = cprm->file; + if (dump_sparse(cprm)) + return dump_skip_frame(cprm, nr); + if (file->f_mode & FMODE_LSEEK) { if (dump_interrupted() || vfs_llseek(file, nr, SEEK_CUR) < 0) return 0; diff --git a/tools/testing/selftests/coredump/coredump_test_helpers.c b/tools/testing/selftests/coredump/coredump_test_helpers.c index bfe10bc51618..ee669969605b 100644 --- a/tools/testing/selftests/coredump/coredump_test_helpers.c +++ b/tools/testing/selftests/coredump/coredump_test_helpers.c @@ -290,7 +290,8 @@ bool send_coredump_ack(int fd, const struct coredump_req *req, /* Every option the kernel is expected to advertise in coredump_req->mask. */ #define COREDUMP_REQ_MASK_ALL \ (COREDUMP_KERNEL | COREDUMP_USERSPACE | \ - COREDUMP_REJECT | COREDUMP_WAIT | COREDUMP_HEADER) + COREDUMP_REJECT | COREDUMP_WAIT | \ + COREDUMP_HEADER | COREDUMP_SPARSE) bool check_coredump_req(const struct coredump_req *req) { -- 2.53.0

