diff mbox

[RFC] bio-integrity: Fix regression if profile verify_fn is NULL

Message ID 20170802122750.12216-1-gmazyland@gmail.com (mailing list archive)
State Accepted, archived
Delegated to: Mike Snitzer
Headers show

Commit Message

Milan Broz Aug. 2, 2017, 12:27 p.m. UTC
In dm-integrity target we register integrity profile that have
both generate_fn and verify_fn callbacks set to NULL.

This is used if dm-integrity is stacked under a dm-crypt device
for authenticated encryption (integrity payload contains authentication
tag and IV seed).

In this case the verification is done through own crypto API
processing inside dm-crypt; integrity profile is only holder
of these data. (And memory is owned by dm-crypt as well.)

After the commit (and previous changes)
  Commit 7c20f11680a441df09de7235206f70115fbf6290
  Author: Christoph Hellwig <hch@lst.de>
  Date:   Mon Jul 3 16:58:43 2017 -0600

    bio-integrity: stop abusing bi_end_io

we get this crash:

: BUG: unable to handle kernel NULL pointer dereference at   (null)
: IP:   (null)
: *pde = 00000000
...
:
: Workqueue: kintegrityd bio_integrity_verify_fn
: task: f48ae180 task.stack: f4b5c000
: EIP:   (null)
: EFLAGS: 00210286 CPU: 0
: EAX: f4b5debc EBX: 00001000 ECX: 00000001 EDX: 00000000
: ESI: 00001000 EDI: ed25f000 EBP: f4b5dee8 ESP: f4b5dea4
:  DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068
: CR0: 80050033 CR2: 00000000 CR3: 32823000 CR4: 001406d0
: Call Trace:
:  ? bio_integrity_process+0xe3/0x1e0
:  bio_integrity_verify_fn+0xea/0x150
:  process_one_work+0x1c7/0x5c0
:  worker_thread+0x39/0x380
:  kthread+0xd6/0x110
:  ? process_one_work+0x5c0/0x5c0
:  ? kthread_worker_fn+0x100/0x100
:  ? kthread_worker_fn+0x100/0x100
:  ret_from_fork+0x19/0x24
: Code:  Bad EIP value.
: EIP:   (null) SS:ESP: 0068:f4b5dea4
: CR2: 0000000000000000

Patch just skip the whole verify workqueue if verify_fn is set to NULL.

Signed-off-by: Milan Broz <gmazyland@gmail.com>
---
 block/bio-integrity.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

Comments

Christoph Hellwig Aug. 2, 2017, 12:55 p.m. UTC | #1
On Wed, Aug 02, 2017 at 02:27:50PM +0200, Milan Broz wrote:
> In dm-integrity target we register integrity profile that have
> both generate_fn and verify_fn callbacks set to NULL.
> 
> This is used if dm-integrity is stacked under a dm-crypt device
> for authenticated encryption (integrity payload contains authentication
> tag and IV seed).
> 
> In this case the verification is done through own crypto API
> processing inside dm-crypt; integrity profile is only holder
> of these data. (And memory is owned by dm-crypt as well.)

Maybe that's where the problem lies?  You're abusing the integrity
payload for something that is not end to end data integrity at all
and then wonder why it breaks?  Also the commit that introduced your
code had absolutely no review by Martin or any of the core block
folks.

The right fix is to revert the dm-crypt commit.

--
dm-devel mailing list
dm-devel@redhat.com
https://www.redhat.com/mailman/listinfo/dm-devel
Milan Broz Aug. 2, 2017, 1:15 p.m. UTC | #2
On 08/02/2017 02:55 PM, Christoph Hellwig wrote:
> On Wed, Aug 02, 2017 at 02:27:50PM +0200, Milan Broz wrote:
>> In dm-integrity target we register integrity profile that have
>> both generate_fn and verify_fn callbacks set to NULL.
>>
>> This is used if dm-integrity is stacked under a dm-crypt device
>> for authenticated encryption (integrity payload contains authentication
>> tag and IV seed).
>>
>> In this case the verification is done through own crypto API
>> processing inside dm-crypt; integrity profile is only holder
>> of these data. (And memory is owned by dm-crypt as well.)
> 
> Maybe that's where the problem lies?  You're abusing the integrity
> payload for something that is not end to end data integrity at all
> and then wonder why it breaks?  Also the commit that introduced your
> code had absolutely no review by Martin or any of the core block
> folks.
> 
> The right fix is to revert the dm-crypt commit.

Hi Christoph,

Why revert? The data there are integrity protection data, just it must to be
processed together with encryption through crypto API call (it is just AEAD,
it must take data + auth tag together).

And the integrity profile is perfect interface for this, we register
own profile through the proper interface.
(Any other solution for per-sector metadata would be worse, I tried...)

And yes, I agree that there should have been review form Martin,
TBH it was not intention to squeeze it there, seems everyone forgot
about this (but the patch went through dm-devel for months).
(Initially it was part of my research work so I care about idea and not patches
just it becomes production code too fast :-)

Anyway, we have it in 4.12 and from my point of view (as cryptsetup
maintainer) this is very needed functionality.

So what do you suggest to do now? I do not think we break anything,
just we need to agree how to use that integrity extension for additional
profiles.

Milan


--
dm-devel mailing list
dm-devel@redhat.com
https://www.redhat.com/mailman/listinfo/dm-devel
Martin K. Petersen Aug. 2, 2017, 2:11 p.m. UTC | #3
Milan,

> And the integrity profile is perfect interface for this, we register
> own profile through the proper interface.  (Any other solution for
> per-sector metadata would be worse, I tried...)

The DM use case seems a bit weird and I would like to take a closer look
later (a storm took out my power and internet so I'm a bit logistically
impaired right now). But the intent of the integrity infrastructure was
to be able to carry arbitrary metadata pinned to an I/O. The callback
hooks in the profile were really only intended for the case where the
block layer itself needed to generate and verify.

We already do sanity checking on the callback pointers in the prep
function. I guess don't have a problem doing the same in the completion
path.

Fundamentally, though, the verify function should only ever be called if
the profile has BLK_INTEGRITY_VERIFY set.

Previously that was done in the prep function by only adding the verify
endio hook when it was needed. Christoph's patch to kill the double
endio changed that subtly (FWIW, Jens originally objected to having an
integrity conditional in the regular bio completion path. That's why the
verification handling abused the endio function).

Anyway. So I think that the BLK_INTEGRITY_VERIFY logic needs to be
carried over to __bio_integrity_endio()...
Milan Broz Aug. 2, 2017, 3:24 p.m. UTC | #4
On 08/02/2017 04:11 PM, Martin K. Petersen wrote:
>> And the integrity profile is perfect interface for this, we register
>> own profile through the proper interface.  (Any other solution for
>> per-sector metadata would be worse, I tried...)
> 
> The DM use case seems a bit weird and I would like to take a closer look
> later (a storm took out my power and internet so I'm a bit logistically
> impaired right now).

Hi Martin,

thanks!

I think it is actually pretty straightforward (if it can be said
about anything in the device-mapper little walled garden :-)

For the authenticated encryption (on the sector level) we cannot use
a length-preserving encryption (as it is dm-crypt in normal mode) but we need
some per-sector metadata to store additional authentication tag.

This is exactly what DIF/T10-PI already does, just we need more flexibility
(and larger metadata to use cryptographically sound modes).

So we developed dm-integrity target that takes a normal block device,
stores per-sector metadata on-disk in special sectors (interleaved with normal
data sectors).
(There is a journalling to provide atomicity of data + metadata writes.)

The dm-integrity can calculate some non-cryptographic integrity data itself,
but this mode does not use block integrity extension at all so it is not important here.

The second use case (that is currently broken by the block layer 4.13 changes)
is that dm-integrity can register a new integrity profile for the virtual device
(on top of normal block device) and present data sectors as normal bio layer and
metadata as the bio-integrity layer (dm-crypt will receive bio with the metadata
in integrity fields).

IOW dm-integrity is just "emulator" for the integrity-enabled block device,
just with configurable per-sector metadata size.

Then we can place dm-crypt above this device and process the bio the same way
as dm-crypt does, just it will add authentication tag mapped to the metadata
for that special integrity profile.

The dm-crypt already creates bio clones (and already owns the bio clone memory)
so we do exactly the same for integrity part of bio (clearing the BIP_BLOCK_INTEGRITY
flag to indicate that block layer should not free this memory).

Otherwise I think we use bio the exact way how the driver should register
and alloc memory for own integrity profile (see dm_crypt_integrity_io_alloc function).

Milan


> But the intent of the integrity infrastructure was
> to be able to carry arbitrary metadata pinned to an I/O. The callback
> hooks in the profile were really only intended for the case where the
> block layer itself needed to generate and verify.
> 
> We already do sanity checking on the callback pointers in the prep
> function. I guess don't have a problem doing the same in the completion
> path.
> 
> Fundamentally, though, the verify function should only ever be called if
> the profile has BLK_INTEGRITY_VERIFY set.
> 
> Previously that was done in the prep function by only adding the verify
> endio hook when it was needed. Christoph's patch to kill the double
> endio changed that subtly (FWIW, Jens originally objected to having an
> integrity conditional in the regular bio completion path. That's why the
> verification handling abused the endio function).
> 
> Anyway. So I think that the BLK_INTEGRITY_VERIFY logic needs to be
> carried over to __bio_integrity_endio()...
> 

--
dm-devel mailing list
dm-devel@redhat.com
https://www.redhat.com/mailman/listinfo/dm-devel
Thorsten Leemhuis Aug. 6, 2017, 1:30 p.m. UTC | #5
TWIMC: This issue is tracked in the regression reports for Linux 4.13
(http://bit.ly/lnxregrep413 ) with this id:

Linux-Regression-ID: lr#35498d

Please include this line in the comment section of patches that are
supposed to fix the issue. Please also mention the string once in other
mailinglist threads or different bug tracking entries if you or someone
else start to discuss the issue there. By including that string you make
it a whole lot easier to track where an issue gets discussed and how far
patches to fix it have made it. For more details on this please see
here: http://bit.ly/lnxregtrackid

Thx for your help. And thx to Milan for pointing me to this regression.
 Ciao, Thorsten

On 02.08.2017 14:27, Milan Broz wrote:
> In dm-integrity target we register integrity profile that have
> both generate_fn and verify_fn callbacks set to NULL.
> 
> This is used if dm-integrity is stacked under a dm-crypt device
> for authenticated encryption (integrity payload contains authentication
> tag and IV seed).
> 
> In this case the verification is done through own crypto API
> processing inside dm-crypt; integrity profile is only holder
> of these data. (And memory is owned by dm-crypt as well.)
> 
> After the commit (and previous changes)
>   Commit 7c20f11680a441df09de7235206f70115fbf6290
>   Author: Christoph Hellwig <hch@lst.de>
>   Date:   Mon Jul 3 16:58:43 2017 -0600
> 
>     bio-integrity: stop abusing bi_end_io
> 
> we get this crash:
> 
> : BUG: unable to handle kernel NULL pointer dereference at   (null)
> : IP:   (null)
> : *pde = 00000000
> ...
> :
> : Workqueue: kintegrityd bio_integrity_verify_fn
> : task: f48ae180 task.stack: f4b5c000
> : EIP:   (null)
> : EFLAGS: 00210286 CPU: 0
> : EAX: f4b5debc EBX: 00001000 ECX: 00000001 EDX: 00000000
> : ESI: 00001000 EDI: ed25f000 EBP: f4b5dee8 ESP: f4b5dea4
> :  DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068
> : CR0: 80050033 CR2: 00000000 CR3: 32823000 CR4: 001406d0
> : Call Trace:
> :  ? bio_integrity_process+0xe3/0x1e0
> :  bio_integrity_verify_fn+0xea/0x150
> :  process_one_work+0x1c7/0x5c0
> :  worker_thread+0x39/0x380
> :  kthread+0xd6/0x110
> :  ? process_one_work+0x5c0/0x5c0
> :  ? kthread_worker_fn+0x100/0x100
> :  ? kthread_worker_fn+0x100/0x100
> :  ret_from_fork+0x19/0x24
> : Code:  Bad EIP value.
> : EIP:   (null) SS:ESP: 0068:f4b5dea4
> : CR2: 0000000000000000
> 
> Patch just skip the whole verify workqueue if verify_fn is set to NULL.
> 
> Signed-off-by: Milan Broz <gmazyland@gmail.com>
> ---
>  block/bio-integrity.c | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
> 
> diff --git a/block/bio-integrity.c b/block/bio-integrity.c
> index 83e92beb3c9f..b9d1580bfc13 100644
> --- a/block/bio-integrity.c
> +++ b/block/bio-integrity.c
> @@ -387,7 +387,9 @@ static void bio_integrity_verify_fn(struct work_struct *work)
>   */
>  bool __bio_integrity_endio(struct bio *bio)
>  {
> -	if (bio_op(bio) == REQ_OP_READ && !bio->bi_status) {
> +	struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev);
> +
> +	if (bi->profile->verify_fn && bio_op(bio) == REQ_OP_READ && !bio->bi_status) {
>  		struct bio_integrity_payload *bip = bio_integrity(bio);
>  
>  		INIT_WORK(&bip->bip_work, bio_integrity_verify_fn);
> 

--
dm-devel mailing list
dm-devel@redhat.com
https://www.redhat.com/mailman/listinfo/dm-devel
diff mbox

Patch

diff --git a/block/bio-integrity.c b/block/bio-integrity.c
index 83e92beb3c9f..b9d1580bfc13 100644
--- a/block/bio-integrity.c
+++ b/block/bio-integrity.c
@@ -387,7 +387,9 @@  static void bio_integrity_verify_fn(struct work_struct *work)
  */
 bool __bio_integrity_endio(struct bio *bio)
 {
-	if (bio_op(bio) == REQ_OP_READ && !bio->bi_status) {
+	struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev);
+
+	if (bi->profile->verify_fn && bio_op(bio) == REQ_OP_READ && !bio->bi_status) {
 		struct bio_integrity_payload *bip = bio_integrity(bio);
 
 		INIT_WORK(&bip->bip_work, bio_integrity_verify_fn);