From christoph.boehmwalder at linbit.com  Fri Jul 10 12:27:28 2026
From: christoph.boehmwalder at linbit.com (Christoph =?utf-8?Q?B=C3=B6hmwalder?=)
Date: Fri, 10 Jul 2026 12:27:28 +0200
Subject: [PATCH] drbd: reject data replies with an out-of-range payload
	size
In-Reply-To: <20260710022837.3738461-1-michael.bommarito@gmail.com>
References: <20260710022837.3738461-1-michael.bommarito@gmail.com>
Message-ID: <alDISEFL7wuNO_VW@localhost.localdomain>

On Thu, Jul 09, 2026 at 10:28:37PM -0400, Michael Bommarito wrote:
>recv_dless_read() receives a P_DATA_REPLY from a peer into the bio of an
>outstanding read request. The peer-supplied payload length reaches it as
>the signed int data_size, and two peer-controlled inputs can make it
>negative. With a negotiated data-integrity-alg the digest length is
>subtracted first, so a reply whose payload is smaller than the digest
>underflows data_size. With no integrity algorithm (the default) data_size
>is assigned from the unsigned h95/h100 wire length and drbdd() never
>bounds it for a payload-carrying command, so a length above INT_MAX casts
>it negative; this path needs no non-default feature. The bio receive loop
>then computes expect = min_t(int, data_size, bv_len), which is negative,
>and drbd_recv_all_warn(mapped, expect) receives with a size_t of SIZE_MAX
>into the first mapped page.
>
>The sibling receive path read_in_block() is not affected: it uses an
>unsigned size and rejects it against DRBD_MAX_BIO_SIZE before receiving.
>Reject a data reply whose size is negative after the optional digest
>subtraction, covering both triggers.
>
>Impact: a malicious or man-in-the-middle DRBD peer copies attacker-chosen
>bytes past a bio page in the receiver, corrupting kernel memory. A node
>that reads from its peer (a diskless node, or read-balancing to the peer)
>is exposed in the default configuration; data-integrity-alg is not
>required.
>
>Fixes: b411b3637fa7 ("The DRBD driver")
>Cc: stable at vger.kernel.org
>Assisted-by: Codex:gpt-5-5-xhigh
>Assisted-by: Claude:claude-opus-4-8
>Signed-off-by: Michael Bommarito <michael.bommarito at gmail.com>

Looks correct, thanks.

Note that DRBD usually chooses to trusts its peers by design, but I
agree that memory corruption is a bad enough consequence that we
should put the guard there regardless.

Reviewed-by: Christoph B?hmwalder <christoph.boehmwalder at linbit.com>

From christoph.boehmwalder at linbit.com  Fri Jul 10 12:34:42 2026
From: christoph.boehmwalder at linbit.com (Christoph =?utf-8?Q?B=C3=B6hmwalder?=)
Date: Fri, 10 Jul 2026 12:34:42 +0200
Subject: [PATCH] drbd: reject oversized DataReply before signed conversion
In-Reply-To: <d4924afb559896a4a22258f14e4a97c8d12d8527@linux.dev>
References: <d4924afb559896a4a22258f14e4a97c8d12d8527@linux.dev>
Message-ID: <alDJWJYBNGpugftk@localhost.localdomain>

On Tue, Jun 30, 2026 at 10:59:31AM +0000, Tianchu Chen wrote:
>From: Tianchu Chen <flynnnchen at tencent.com>
>
>Discovered by Atuin - Automated Vulnerability Discovery Engine.
>
>Reject DataReply payload lengths that cannot fit in recv_dless_read()'s
>signed size argument so a bogus remote peer cannot wrap the length negative
>and turn it into a huge heap OOB-write.
>
>Fixes: b411b3637fa7 ("The DRBD driver")
>Cc: stable at vger.kernel.org
>Signed-off-by: Tianchu Chen <flynnnchen at tencent.com>

This is similar to [0], but the mentioned patch is a more complete fix. 
There is also a case in recv_dless_read where data_size can underflow 
when a data-integrity-alg is configured.

The other patch catches this case as well by placing the guard later, so 
I would prefer to apply that one instead of this.

Thanks,
Christoph

[0] https://lore.kernel.org/all/20260710022837.3738461-1-michael.bommarito at gmail.com/

From christoph.boehmwalder at linbit.com  Fri Jul 10 13:13:45 2026
From: christoph.boehmwalder at linbit.com (Christoph =?utf-8?Q?B=C3=B6hmwalder?=)
Date: Fri, 10 Jul 2026 13:13:45 +0200
Subject: [PATCH] drbd: Fix potential NULL pointer dereference in
	_drbd_set_state()
In-Reply-To: <20260625050016.12004-1-a.vatoropin@crpt.ru>
References: <20260625050016.12004-1-a.vatoropin@crpt.ru>
Message-ID: <alDRuDisKeheSGc8@linbit.com>

Thanks for your patch.

On Thu, Jun 25, 2026 at 05:03:06AM +0000, ????????? ?????? wrote:
>From: Andrey Vatoropin <a.vatoropin at crpt.ru>
>
>The connection pointer receives a value in the _drbd_set_state()
>function, including through a call to the first_peer_device() function.
>This function returns a pointer to a list element. If the list is empty, it
>returns a NULL pointer, which is later assigned to the connection
>pointer. Subsequently, this pointer will be dereferenced.

Can the list actually be empty at this point?
The peer_device is linked into the list in drbd_create_device(), before
add_disk() and before the device is inserted into connection->peer_devices,
so no state change can reach the device earlier.

It is only unlinked again in drbd_destroy_device(), after the last kref
to the device is gone.
The connection itself is created together with the resource in
drbd_adm_new_resource() and lives until the resource is destroyed.

So for any device this function can be called on, first_peer_device()
returns a valid peer_device.

>
>Add a NULL check for the connection pointer to avoid dereferencing an
>invalid pointer.
>
>Found by Linux Verification Center (linuxtesting.org) with SVACE.
>
>Fixes: a6b32bc3cebd ("drbd: Introduce "peer_device" object between "device" and "connection"")
>Cc: stable at vger.kernel.org
>Signed-off-by: Andrey Vatoropin <a.vatoropin at crpt.ru>
>---
> drivers/block/drbd/drbd_state.c | 5 +++++
> 1 file changed, 5 insertions(+)
>
>diff --git a/drivers/block/drbd/drbd_state.c b/drivers/block/drbd/drbd_state.c
>index adcba7f1d8ea..ea982d48017e 100644
>--- a/drivers/block/drbd/drbd_state.c
>+++ b/drivers/block/drbd/drbd_state.c
>@@ -1281,6 +1281,11 @@ _drbd_set_state(struct drbd_device *device, union drbd_state ns,
> 	if (rv < SS_SUCCESS)
> 		return rv;
>
>+	if (!connection) {
>+		drbd_err(device, "No connection to peer, aborting!\n");
>+		return SS_ALREADY_STANDALONE;
>+	}
>+

Also, even if the condition could happen, its handling here would be 
wrong. Since this check happens before handling hard state changes, 
those could potentially be skipped, which is not allowed.
For example, after a local I/O error (drbd_chk_io_error), if this 
condition would trigger, the detach state change would be silently 
skipped. So in that circumstance, this patch would be actively harmful.

Also, SS_ALREADY_STANDALONE would map to the error message "Can not
disconnect a StandAlone device", which does not make any sense in this 
context.

> 	if (!(flags & CS_HARD)) {
> 		/*  pre-state-change checks ; only look at ns  */
> 		/* See drbd_state_sw_errors in drbd_strings.c */
>-- 
>2.43.0

In summary, unless I missed something major: NAK.

Thanks,
Christoph

From axboe at kernel.dk  Fri Jul 10 17:09:05 2026
From: axboe at kernel.dk (Jens Axboe)
Date: Fri, 10 Jul 2026 09:09:05 -0600
Subject: [PATCH] drbd: reject data replies with an out-of-range payload
	size
In-Reply-To: <20260710022837.3738461-1-michael.bommarito@gmail.com>
References: <20260710022837.3738461-1-michael.bommarito@gmail.com>
Message-ID: <178369614511.284240.12733294719230479134.b4-ty@b4>


On Thu, 09 Jul 2026 22:28:37 -0400, Michael Bommarito wrote:
> recv_dless_read() receives a P_DATA_REPLY from a peer into the bio of an
> outstanding read request. The peer-supplied payload length reaches it as
> the signed int data_size, and two peer-controlled inputs can make it
> negative. With a negotiated data-integrity-alg the digest length is
> subtracted first, so a reply whose payload is smaller than the digest
> underflows data_size. With no integrity algorithm (the default) data_size
> is assigned from the unsigned h95/h100 wire length and drbdd() never
> bounds it for a payload-carrying command, so a length above INT_MAX casts
> it negative; this path needs no non-default feature. The bio receive loop
> then computes expect = min_t(int, data_size, bv_len), which is negative,
> and drbd_recv_all_warn(mapped, expect) receives with a size_t of SIZE_MAX
> into the first mapped page.
> 
> [...]

Applied, thanks!

[1/1] drbd: reject data replies with an out-of-range payload size
      commit: bd910a7660d280595ef94cb6d193951d855d330f

Best regards,
-- 
Jens Axboe




From christoph.boehmwalder at linbit.com  Fri Jul 24 18:09:11 2026
From: christoph.boehmwalder at linbit.com (Christoph =?utf-8?Q?B=C3=B6hmwalder?=)
Date: Fri, 24 Jul 2026 18:09:11 +0200
Subject: [PATCH] drbd: Fix double put_ldev in receive_SyncParam on
	fifo_alloc failure
In-Reply-To: <20260625150417.69968-1-vulab@iscas.ac.cn>
References: <20260625150417.69968-1-vulab@iscas.ac.cn>
Message-ID: <amONUAJL4EZZTFKF@linbit.com>

On Thu, Jun 25, 2026 at 11:04:17PM +0800, Wentao Liang wrote:
>In receive_SyncParam(), when the fifo_alloc() call for the resync
>plan buffer fails, the error path executes put_ldev(device) at line
>3790 and then jumps to the disconnect label. The disconnect label
>also calls put_ldev(device) when new_disk_conf is non-NULL, which
>is always the case by this point (get_ldev succeeded and
>new_disk_conf was allocated).
>
>This results in a double put_ldev, causing the ldev reference count
>to underflow. All other goto disconnect sites in the same function
>correctly rely solely on the disconnect label to perform the single
>put_ldev ? the fifo_alloc failure path was the only one to
>prematurely release the reference.
>
>Remove the spurious put_ldev(device) call before goto disconnect
>to fix the double put.
>
>Cc: stable at vger.kernel.org
>Fixes: b30ab7913b0a ("drbd: Rename "mdev" to "device"")
>Signed-off-by: Wentao Liang <vulab at iscas.ac.cn>
>---
> drivers/block/drbd/drbd_receiver.c | 1 -
> 1 file changed, 1 deletion(-)
>
>diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c
>index 58b95bf4bdca..f618d03fd2a6 100644
>--- a/drivers/block/drbd/drbd_receiver.c
>+++ b/drivers/block/drbd/drbd_receiver.c
>@@ -3787,7 +3787,6 @@ static int receive_SyncParam(struct drbd_connection *connection, struct packet_i
> 				new_plan = fifo_alloc(fifo_size);
> 				if (!new_plan) {
> 					drbd_err(device, "kmalloc of fifo_buffer failed");
>-					put_ldev(device);
> 					goto disconnect;
> 				}
> 			}
>-- 
>2.39.5 (Apple Git-154)

Thanks for the patch, the fix itself looks correct to me.

The Fixes tag points at the wrong commit though. b30ab7913b0a is a
purely mechanical rename and only carried the existing code forward.

The double put was actually introduced by 813472ced7fa ("drbd: RCU for
rs_plan_s"), which added the put_ldev() to the disconnect label.

So the tag should be:

Fixes: 813472ced7fa ("drbd: RCU for rs_plan_s")

With that fixed up:

Reviewed-by: Christoph B?hmwalder <christoph.boehmwalder at linbit.com>

From christoph.boehmwalder at linbit.com  Fri Jul 24 18:33:13 2026
From: christoph.boehmwalder at linbit.com (Christoph =?utf-8?Q?B=C3=B6hmwalder?=)
Date: Fri, 24 Jul 2026 18:33:13 +0200
Subject: [PATCH] drbd: Fix local_cnt refcount leak on ascw allocation
	failure in _drbd_set_state
In-Reply-To: <20260625151636.72599-1-vulab@iscas.ac.cn>
References: <20260625151636.72599-1-vulab@iscas.ac.cn>
Message-ID: <amOR6AGwUp6Blsdl@linbit.com>

On Thu, Jun 25, 2026 at 11:16:36PM +0800, Wentao Liang wrote:
>In _drbd_set_state(), when transitioning a device to D_FAILED or
>D_DISKLESS, an extra reference on local_cnt is taken via
>atomic_inc(&device->local_cnt) to prevent premature destruction of
>the local disk. This reference is normally released by put_ldev()
>in after_state_ch(), which is called asynchronously through the
>after_state_chg_work (ascw) work item.
>
>If the GFP_ATOMIC allocation of the ascw work item fails, the work
>is never queued, after_state_ch() never runs, and the extra
>local_cnt reference is permanently leaked. Additionally, the
>state_change object allocated by remember_old_state() is also
>leaked, along with the krefs it acquired on the resource,
>connections, and devices.
>
>Fix both leaks in the ascw allocation failure path:
> - Call put_ldev() to release the extra local_cnt reference when
>   the transition matches the same conditions used for the
>   atomic_inc.
> - Call forget_state_change() to free the state_change object and
>   release the krefs it holds.
>
>Cc: stable at vger.kernel.org
>Fixes: d01801710265 ("drbd: Remove the terrible DEV hack")
>Signed-off-by: Wentao Liang <vulab at iscas.ac.cn>
>---
> drivers/block/drbd/drbd_state.c | 8 +++++++-
> 1 file changed, 7 insertions(+), 1 deletion(-)
>
>diff --git a/drivers/block/drbd/drbd_state.c b/drivers/block/drbd/drbd_state.c
>index adcba7f1d8ea..68e273c6d5be 100644
>--- a/drivers/block/drbd/drbd_state.c
>+++ b/drivers/block/drbd/drbd_state.c
>@@ -1480,7 +1480,13 @@ _drbd_set_state(struct drbd_device *device, union drbd_state ns,
> 		drbd_queue_work(&connection->sender_work,
> 				&ascw->w);
> 	} else {
>-		drbd_err(device, "Could not kmalloc an ascw\n");
>+		if ((os.disk != D_FAILED && ns.disk == D_FAILED) ||
>+		    (os.disk != D_DISKLESS && ns.disk == D_DISKLESS))
>+			put_ldev(device);

Thanks for the patch. The logic itself looks correct to me.

>+
>+		forget_state_change(state_change);
>+		drbd_err(device, "Could not kmalloc an ascw, state change %p -> %p leaked\n",
>+			 &os, &ns);

However, this error message is nonsensical. If anything, we should print 
some halfway human-readable identifier for the state values here, not 
the pointer.

Also, the state change is precisely *not* leaked at the point this
message triggers, since we free it here. What actually gets lost is the 
effects of the state change, so if anything we should point that out 
here. But I think just keeping the original message is fine.

> 	}
>
> 	return rv;
>-- 
>2.39.5 (Apple Git-154)

Also, the Fixes tag points to the wrong commit, that was just a 
mechanical change. The actual breakage was introduced in commit
82f59cc63538 ("drbd: fix potential deadlock on detach").

