summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rettberg2025-04-15 16:46:04 +0200
committerSimon Rettberg2025-04-15 17:31:20 +0200
commita853b643bdfdd15d4bcf1e188ba7fb56362520a2 (patch)
tree12270d3f9583e476782bed6863c156362114d33e
parentREADME: Add curl-devel for CentOS-based requirements (diff)
downloaddnbd3-a853b643bdfdd15d4bcf1e188ba7fb56362520a2.tar.gz
dnbd3-a853b643bdfdd15d4bcf1e188ba7fb56362520a2.tar.xz
dnbd3-a853b643bdfdd15d4bcf1e188ba7fb56362520a2.zip
[KERNEL] Check if reply size matches request
Output according messages and abort connection if the reply didn't contain exactly as many bytes as requested, instead of going on and running into desync with the reply stream from the server, which would just give us confusing messages about header magic mismatch.
-rw-r--r--src/kernel/net.c45
1 files changed, 32 insertions, 13 deletions
diff --git a/src/kernel/net.c b/src/kernel/net.c
index 5501309..06e20aa 100644
--- a/src/kernel/net.c
+++ b/src/kernel/net.c
@@ -467,7 +467,7 @@ static void dnbd3_recv_workfn(struct work_struct *work)
void *kaddr;
unsigned long irqflags;
uint16_t rid;
- int remaining;
+ u32 remaining;
int ret;
dnbd3_dev_dbg_cur(dev, "starting receive worker...\n");
@@ -528,16 +528,24 @@ static void dnbd3_recv_workfn(struct work_struct *work)
goto out_unlock;
}
// receive data and answer to block layer
+ remaining = reply_hdr.size;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
rq_for_each_segment(bvec_inst, blk_request, iter) {
#else
rq_for_each_segment(bvec, blk_request, iter) {
#endif
- kaddr = kmap(bvec->bv_page) + bvec->bv_offset;
- iov.iov_base = kaddr;
- iov.iov_len = bvec->bv_len;
- ret = kernel_recvmsg(dev->sock, &msg, &iov, 1, bvec->bv_len, msg.msg_flags);
- kunmap(bvec->bv_page);
+ if (bvec->bv_len > remaining) {
+ dnbd3_dev_dbg_cur(
+ dev, "request has more data remaining than is left in reply (want: %u, have: %u)\n",
+ bvec->bv_len, remaining);
+ ret = -1;
+ } else {
+ kaddr = kmap(bvec->bv_page) + bvec->bv_offset;
+ iov.iov_base = kaddr;
+ iov.iov_len = bvec->bv_len;
+ ret = kernel_recvmsg(dev->sock, &msg, &iov, 1, bvec->bv_len, msg.msg_flags);
+ kunmap(bvec->bv_page);
+ }
if (ret != bvec->bv_len) {
if (ret == 0) {
/* have not received any data, but remote peer is shutdown properly */
@@ -546,18 +554,29 @@ static void dnbd3_recv_workfn(struct work_struct *work)
} else if (ret < 0) {
if (!dnbd3_flag_taken(dev->connection_lock))
dnbd3_dev_err_cur(dev,
- "disconnect: receiving from net to block layer\n");
+ "receiving from net to block layer failed (ret=%d)\n", ret);
} else {
if (!dnbd3_flag_taken(dev->connection_lock))
dnbd3_dev_err_cur(dev,
"receiving from net to block layer (%d bytes)\n", ret);
}
- // Requeue request
- spin_lock_irqsave(&dev->send_queue_lock, irqflags);
- list_add(&blk_request->queuelist, &dev->send_queue);
- spin_unlock_irqrestore(&dev->send_queue_lock, irqflags);
- goto out_unlock;
+ goto segment_loop_end;
}
+ remaining -= ret;
+ }
+segment_loop_end: /* Make this a goto as rq_for_each_segment is opaque and can be any number of nested loops */
+ if (remaining != 0) {
+ if (ret > 0) {
+ /* No previous error, the reply must've had more payload than the according request */
+ dnbd3_dev_err_cur(dev,
+ "reply has payload left, but block request already satisfied (len: %u, remaining: %u)\n",
+ reply_hdr.size, remaining);
+ }
+ // Requeue request
+ spin_lock_irqsave(&dev->send_queue_lock, irqflags);
+ list_add(&blk_request->queuelist, &dev->send_queue);
+ spin_unlock_irqrestore(&dev->send_queue_lock, irqflags);
+ goto out_unlock;
}
blk_mq_end_request(blk_request, BLK_STS_OK);
break;
@@ -567,7 +586,7 @@ static void dnbd3_recv_workfn(struct work_struct *work)
if (dev->use_server_provided_alts) {
dnbd3_server_entry_t new_server;
- while (remaining >= sizeof(dnbd3_server_entry_t)) {
+ while (remaining >= sizeof(new_server)) {
if (dnbd3_recv_bytes(dev->sock, &new_server, sizeof(new_server))
!= sizeof(new_server)) {
if (!dnbd3_flag_taken(dev->connection_lock))