Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1614928 > unrolled thread

[PATCH 7/7] Guard bvec iteration logic v2

Started byDmitry Monakhov <dmonakhov@openvz.org>
First post2017-04-03 09:30 +0200
Last post2017-04-04 18:00 +0200
Articles 5 — 3 participants

Back to article view | Back to linux.kernel

This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by below is the oldest one visible, not the original post.


Contents

  [PATCH 7/7] Guard bvec iteration logic v2 Dmitry Monakhov <dmonakhov@openvz.org> - 2017-04-03 09:30 +0200
    Re: [PATCH 7/7] Guard bvec iteration logic v2 Jens Axboe <axboe@kernel.dk> - 2017-04-03 16:40 +0200
    Re: [PATCH 7/7] Guard bvec iteration logic v2 Ming Lei <tom.leiming@gmail.com> - 2017-04-04 17:10 +0200
      Re: [PATCH 7/7] Guard bvec iteration logic v2 Dmitry Monakhov <dmonakhov@openvz.org> - 2017-04-04 17:30 +0200
        Re: [PATCH 7/7] Guard bvec iteration logic v2 Ming Lei <tom.leiming@gmail.com> - 2017-04-04 18:00 +0200

#1614928 — [PATCH 7/7] Guard bvec iteration logic v2

FromDmitry Monakhov <dmonakhov@openvz.org>
Date2017-04-03 09:30 +0200
Subject[PATCH 7/7] Guard bvec iteration logic v2
Message-ID<ts4B4-3Mv-19@gated-at.bofh.it>
Currently if some one try to advance bvec beyond it's size we simply
dump WARN_ONCE and continue to iterate beyond bvec array boundaries.
This simply means that we endup dereferencing/corrupting random memory
region.

Sane reaction would be to propagate error back to calling context
But bvec_iter_advance's calling context is not always good for error
handling. For safity reason let truncate iterator size to zero which
will break external iteration loop which prevent us from unpredictable
memory range corruption. And even it caller ignores an error, it will
corrupt it's own bvecs, not others.

This patch does:
- Return error back to caller with hope that it will react on this
- Truncate iterator size

Code was added long time ago here 4550dd6c, luckily no one hit it
in real life :)

changes since V1:
 - Replace  BUG_ON with error logic.

Signed-off-by: Dmitry Monakhov <dmonakhov@openvz.org>
---
 drivers/nvdimm/blk.c |  4 +++-
 drivers/nvdimm/btt.c |  4 +++-
 include/linux/bio.h  |  8 ++++++--
 include/linux/bvec.h | 11 ++++++++---
 4 files changed, 20 insertions(+), 7 deletions(-)

diff --git a/drivers/nvdimm/blk.c b/drivers/nvdimm/blk.c
index 1edb3f3..04c3075 100644
--- a/drivers/nvdimm/blk.c
+++ b/drivers/nvdimm/blk.c
@@ -106,7 +106,9 @@ static int nd_blk_rw_integrity(struct nd_namespace_blk *nsblk,
 
 		len -= cur_len;
 		dev_offset += cur_len;
-		bvec_iter_advance(bip->bip_vec, &bip->bip_iter, cur_len);
+		err = bvec_iter_advance(bip->bip_vec, &bip->bip_iter, cur_len);
+		if (err)
+			return err;
 	}
 
 	return err;
diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c
index 03ded8d..3f3aa7b 100644
--- a/drivers/nvdimm/btt.c
+++ b/drivers/nvdimm/btt.c
@@ -942,7 +942,9 @@ static int btt_rw_integrity(struct btt *btt, struct bio_integrity_payload *bip,
 
 		len -= cur_len;
 		meta_nsoff += cur_len;
-		bvec_iter_advance(bip->bip_vec, &bip->bip_iter, cur_len);
+		ret = bvec_iter_advance(bip->bip_vec, &bip->bip_iter, cur_len);
+		if (ret)
+			return ret;
 	}
 
 	return ret;
diff --git a/include/linux/bio.h b/include/linux/bio.h
index 0c1c95c..8bf1564 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -168,8 +168,12 @@ static inline void bio_advance_iter(struct bio *bio, struct bvec_iter *iter,
 
 	if (bio_no_advance_iter(bio))
 		iter->bi_size -= bytes;
-	else
-		bvec_iter_advance(bio->bi_io_vec, iter, bytes);
+	else {
+		int err;
+		err = bvec_iter_advance(bio->bi_io_vec, iter, bytes);
+		if (unlikely(err))
+			bio->bi_error = err;
+	}
 }
 
 #define __bio_for_each_segment(bvl, bio, iter, start)			\
diff --git a/include/linux/bvec.h b/include/linux/bvec.h
index 89b65b8..c117f1a 100644
--- a/include/linux/bvec.h
+++ b/include/linux/bvec.h
@@ -22,6 +22,7 @@
 
 #include <linux/kernel.h>
 #include <linux/bug.h>
+#include <linux/errno.h>
 
 /*
  * was unsigned short, but we might as well be ready for > 64kB I/O pages
@@ -66,12 +67,15 @@ struct bvec_iter {
 	.bv_offset	= bvec_iter_offset((bvec), (iter)),	\
 })
 
-static inline void bvec_iter_advance(const struct bio_vec *bv,
+static inline int bvec_iter_advance(const struct bio_vec *bv,
 				     struct bvec_iter *iter,
 				     unsigned bytes)
 {
-	WARN_ONCE(bytes > iter->bi_size,
-		  "Attempted to advance past end of bvec iter\n");
+	if(unlikely(bytes > iter->bi_size)) {
+		WARN(1, "Attempted to advance past end of bvec iter\n");
+		iter->bi_size = 0;
+		return -EINVAL;
+	}
 
 	while (bytes) {
 		unsigned iter_len = bvec_iter_len(bv, *iter);
@@ -86,6 +90,7 @@ static inline void bvec_iter_advance(const struct bio_vec *bv,
 			iter->bi_idx++;
 		}
 	}
+	return 0;
 }
 
 #define for_each_bvec(bvl, bio_vec, iter, start)			\
-- 
2.9.3

[toc] | [next] | [standalone]


#1615252

FromJens Axboe <axboe@kernel.dk>
Date2017-04-03 16:40 +0200
Message-ID<tsbjb-84l-5@gated-at.bofh.it>
In reply to#1614928
On 04/03/2017 01:23 AM, Dmitry Monakhov wrote:
> @@ -66,12 +67,15 @@ struct bvec_iter {
>  	.bv_offset	= bvec_iter_offset((bvec), (iter)),	\
>  })
>  
> -static inline void bvec_iter_advance(const struct bio_vec *bv,
> +static inline int bvec_iter_advance(const struct bio_vec *bv,
>  				     struct bvec_iter *iter,
>  				     unsigned bytes)
>  {
> -	WARN_ONCE(bytes > iter->bi_size,
> -		  "Attempted to advance past end of bvec iter\n");
> +	if(unlikely(bytes > iter->bi_size)) {
> +		WARN(1, "Attempted to advance past end of bvec iter\n");
> +		iter->bi_size = 0;
> +		return -EINVAL;
> +	}

	if (WARN_ONCE(bytes > iter->bi_size,
			"Attempted to advance past end of bvec iter\n")) {
		...

would be cleaner.

-- 
Jens Axboe

[toc] | [prev] | [next] | [standalone]


#1616087

FromMing Lei <tom.leiming@gmail.com>
Date2017-04-04 17:10 +0200
Message-ID<tsyfM-6vk-3@gated-at.bofh.it>
In reply to#1614928
On Mon, Apr 3, 2017 at 3:23 PM, Dmitry Monakhov <dmonakhov@openvz.org> wrote:
> Currently if some one try to advance bvec beyond it's size we simply
> dump WARN_ONCE and continue to iterate beyond bvec array boundaries.
> This simply means that we endup dereferencing/corrupting random memory
> region.
>
> Sane reaction would be to propagate error back to calling context
> But bvec_iter_advance's calling context is not always good for error
> handling. For safity reason let truncate iterator size to zero which

IMO, we can avoid continuing to iterate by checking the return value,
and looks it is rude to just set iterator size as 0.

> will break external iteration loop which prevent us from unpredictable
> memory range corruption. And even it caller ignores an error, it will
> corrupt it's own bvecs, not others.
>
> This patch does:
> - Return error back to caller with hope that it will react on this
> - Truncate iterator size
>
> Code was added long time ago here 4550dd6c, luckily no one hit it
> in real life :)
>
> changes since V1:
>  - Replace  BUG_ON with error logic.
>
> Signed-off-by: Dmitry Monakhov <dmonakhov@openvz.org>
> ---
>  drivers/nvdimm/blk.c |  4 +++-
>  drivers/nvdimm/btt.c |  4 +++-
>  include/linux/bio.h  |  8 ++++++--
>  include/linux/bvec.h | 11 ++++++++---
>  4 files changed, 20 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/nvdimm/blk.c b/drivers/nvdimm/blk.c
> index 1edb3f3..04c3075 100644
> --- a/drivers/nvdimm/blk.c
> +++ b/drivers/nvdimm/blk.c
> @@ -106,7 +106,9 @@ static int nd_blk_rw_integrity(struct nd_namespace_blk *nsblk,
>
>                 len -= cur_len;
>                 dev_offset += cur_len;
> -               bvec_iter_advance(bip->bip_vec, &bip->bip_iter, cur_len);
> +               err = bvec_iter_advance(bip->bip_vec, &bip->bip_iter, cur_len);
> +               if (err)
> +                       return err;
>         }
>
>         return err;
> diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c
> index 03ded8d..3f3aa7b 100644
> --- a/drivers/nvdimm/btt.c
> +++ b/drivers/nvdimm/btt.c
> @@ -942,7 +942,9 @@ static int btt_rw_integrity(struct btt *btt, struct bio_integrity_payload *bip,
>
>                 len -= cur_len;
>                 meta_nsoff += cur_len;
> -               bvec_iter_advance(bip->bip_vec, &bip->bip_iter, cur_len);
> +               ret = bvec_iter_advance(bip->bip_vec, &bip->bip_iter, cur_len);
> +               if (ret)
> +                       return ret;
>         }
>
>         return ret;
> diff --git a/include/linux/bio.h b/include/linux/bio.h
> index 0c1c95c..8bf1564 100644
> --- a/include/linux/bio.h
> +++ b/include/linux/bio.h
> @@ -168,8 +168,12 @@ static inline void bio_advance_iter(struct bio *bio, struct bvec_iter *iter,
>
>         if (bio_no_advance_iter(bio))
>                 iter->bi_size -= bytes;
> -       else
> -               bvec_iter_advance(bio->bi_io_vec, iter, bytes);
> +       else {
> +               int err;
> +               err = bvec_iter_advance(bio->bi_io_vec, iter, bytes);
> +               if (unlikely(err))
> +                       bio->bi_error = err;
> +       }
>  }
>
>  #define __bio_for_each_segment(bvl, bio, iter, start)                  \
> diff --git a/include/linux/bvec.h b/include/linux/bvec.h
> index 89b65b8..c117f1a 100644
> --- a/include/linux/bvec.h
> +++ b/include/linux/bvec.h
> @@ -22,6 +22,7 @@
>
>  #include <linux/kernel.h>
>  #include <linux/bug.h>
> +#include <linux/errno.h>
>
>  /*
>   * was unsigned short, but we might as well be ready for > 64kB I/O pages
> @@ -66,12 +67,15 @@ struct bvec_iter {
>         .bv_offset      = bvec_iter_offset((bvec), (iter)),     \
>  })
>
> -static inline void bvec_iter_advance(const struct bio_vec *bv,
> +static inline int bvec_iter_advance(const struct bio_vec *bv,
>                                      struct bvec_iter *iter,
>                                      unsigned bytes)
>  {
> -       WARN_ONCE(bytes > iter->bi_size,
> -                 "Attempted to advance past end of bvec iter\n");
> +       if(unlikely(bytes > iter->bi_size)) {
> +               WARN(1, "Attempted to advance past end of bvec iter\n");
> +               iter->bi_size = 0;
> +               return -EINVAL;
> +       }
>
>         while (bytes) {
>                 unsigned iter_len = bvec_iter_len(bv, *iter);
> @@ -86,6 +90,7 @@ static inline void bvec_iter_advance(const struct bio_vec *bv,
>                         iter->bi_idx++;
>                 }
>         }
> +       return 0;
>  }
>
>  #define for_each_bvec(bvl, bio_vec, iter, start)                       \
> --
> 2.9.3
>



-- 
Ming Lei

[toc] | [prev] | [next] | [standalone]


#1616104

FromDmitry Monakhov <dmonakhov@openvz.org>
Date2017-04-04 17:30 +0200
Message-ID<tsyz8-6Dw-21@gated-at.bofh.it>
In reply to#1616087
Ming Lei <tom.leiming@gmail.com> writes:

> On Mon, Apr 3, 2017 at 3:23 PM, Dmitry Monakhov <dmonakhov@openvz.org> wrote:
>> Currently if some one try to advance bvec beyond it's size we simply
>> dump WARN_ONCE and continue to iterate beyond bvec array boundaries.
>> This simply means that we endup dereferencing/corrupting random memory
>> region.
>>
>> Sane reaction would be to propagate error back to calling context
>> But bvec_iter_advance's calling context is not always good for error
>> handling. For safity reason let truncate iterator size to zero which
>
> IMO, we can avoid continuing to iterate by checking the return value,
> and looks it is rude to just set iterator size as 0.
But situation itself is horrible already. IMHO this is BUG_ON situation,
but since Linus hate bugons, I try to replace with something loud, but
very safe. Since there is no guarantee that caller will ignore an error
and try to dereference bvec the only safe thing we can to is to clamp
iterator to zero to prevent any possible usage in future.
>
>> will break external iteration loop which prevent us from unpredictable
>> memory range corruption. And even it caller ignores an error, it will
>> corrupt it's own bvecs, not others.
>>
>> This patch does:
>> - Return error back to caller with hope that it will react on this
>> - Truncate iterator size
>>
>> Code was added long time ago here 4550dd6c, luckily no one hit it
>> in real life :)
>>
>> changes since V1:
>>  - Replace  BUG_ON with error logic.
>>
>> Signed-off-by: Dmitry Monakhov <dmonakhov@openvz.org>
>> ---
>>  drivers/nvdimm/blk.c |  4 +++-
>>  drivers/nvdimm/btt.c |  4 +++-
>>  include/linux/bio.h  |  8 ++++++--
>>  include/linux/bvec.h | 11 ++++++++---
>>  4 files changed, 20 insertions(+), 7 deletions(-)
>>
>> diff --git a/drivers/nvdimm/blk.c b/drivers/nvdimm/blk.c
>> index 1edb3f3..04c3075 100644
>> --- a/drivers/nvdimm/blk.c
>> +++ b/drivers/nvdimm/blk.c
>> @@ -106,7 +106,9 @@ static int nd_blk_rw_integrity(struct nd_namespace_blk *nsblk,
>>
>>                 len -= cur_len;
>>                 dev_offset += cur_len;
>> -               bvec_iter_advance(bip->bip_vec, &bip->bip_iter, cur_len);
>> +               err = bvec_iter_advance(bip->bip_vec, &bip->bip_iter, cur_len);
>> +               if (err)
>> +                       return err;
>>         }
>>
>>         return err;
>> diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c
>> index 03ded8d..3f3aa7b 100644
>> --- a/drivers/nvdimm/btt.c
>> +++ b/drivers/nvdimm/btt.c
>> @@ -942,7 +942,9 @@ static int btt_rw_integrity(struct btt *btt, struct bio_integrity_payload *bip,
>>
>>                 len -= cur_len;
>>                 meta_nsoff += cur_len;
>> -               bvec_iter_advance(bip->bip_vec, &bip->bip_iter, cur_len);
>> +               ret = bvec_iter_advance(bip->bip_vec, &bip->bip_iter, cur_len);
>> +               if (ret)
>> +                       return ret;
>>         }
>>
>>         return ret;
>> diff --git a/include/linux/bio.h b/include/linux/bio.h
>> index 0c1c95c..8bf1564 100644
>> --- a/include/linux/bio.h
>> +++ b/include/linux/bio.h
>> @@ -168,8 +168,12 @@ static inline void bio_advance_iter(struct bio *bio, struct bvec_iter *iter,
>>
>>         if (bio_no_advance_iter(bio))
>>                 iter->bi_size -= bytes;
>> -       else
>> -               bvec_iter_advance(bio->bi_io_vec, iter, bytes);
>> +       else {
>> +               int err;
>> +               err = bvec_iter_advance(bio->bi_io_vec, iter, bytes);
>> +               if (unlikely(err))
>> +                       bio->bi_error = err;
>> +       }
>>  }
>>
>>  #define __bio_for_each_segment(bvl, bio, iter, start)                  \
>> diff --git a/include/linux/bvec.h b/include/linux/bvec.h
>> index 89b65b8..c117f1a 100644
>> --- a/include/linux/bvec.h
>> +++ b/include/linux/bvec.h
>> @@ -22,6 +22,7 @@
>>
>>  #include <linux/kernel.h>
>>  #include <linux/bug.h>
>> +#include <linux/errno.h>
>>
>>  /*
>>   * was unsigned short, but we might as well be ready for > 64kB I/O pages
>> @@ -66,12 +67,15 @@ struct bvec_iter {
>>         .bv_offset      = bvec_iter_offset((bvec), (iter)),     \
>>  })
>>
>> -static inline void bvec_iter_advance(const struct bio_vec *bv,
>> +static inline int bvec_iter_advance(const struct bio_vec *bv,
>>                                      struct bvec_iter *iter,
>>                                      unsigned bytes)
>>  {
>> -       WARN_ONCE(bytes > iter->bi_size,
>> -                 "Attempted to advance past end of bvec iter\n");
>> +       if(unlikely(bytes > iter->bi_size)) {
>> +               WARN(1, "Attempted to advance past end of bvec iter\n");
>> +               iter->bi_size = 0;
>> +               return -EINVAL;
>> +       }
>>
>>         while (bytes) {
>>                 unsigned iter_len = bvec_iter_len(bv, *iter);
>> @@ -86,6 +90,7 @@ static inline void bvec_iter_advance(const struct bio_vec *bv,
>>                         iter->bi_idx++;
>>                 }
>>         }
>> +       return 0;
>>  }
>>
>>  #define for_each_bvec(bvl, bio_vec, iter, start)                       \
>> --
>> 2.9.3
>>
>
>
>
> -- 
> Ming Lei

[toc] | [prev] | [next] | [standalone]


#1616149

FromMing Lei <tom.leiming@gmail.com>
Date2017-04-04 18:00 +0200
Message-ID<tsz2a-6Qd-9@gated-at.bofh.it>
In reply to#1616104
On Tue, Apr 4, 2017 at 11:19 PM, Dmitry Monakhov <dmonakhov@openvz.org> wrote:
> Ming Lei <tom.leiming@gmail.com> writes:
>
>> On Mon, Apr 3, 2017 at 3:23 PM, Dmitry Monakhov <dmonakhov@openvz.org> wrote:
>>> Currently if some one try to advance bvec beyond it's size we simply
>>> dump WARN_ONCE and continue to iterate beyond bvec array boundaries.
>>> This simply means that we endup dereferencing/corrupting random memory
>>> region.
>>>
>>> Sane reaction would be to propagate error back to calling context
>>> But bvec_iter_advance's calling context is not always good for error
>>> handling. For safity reason let truncate iterator size to zero which
>>
>> IMO, we can avoid continuing to iterate by checking the return value,
>> and looks it is rude to just set iterator size as 0.
> But situation itself is horrible already. IMHO this is BUG_ON situation,

Not sure it is a real BUG_ON() since corrupt bvec array shouldn't happen
because we usually don't modify bvec array directly and just copy to local
variable for further access, but dereferencing random memory might happen.

> but since Linus hate bugons, I try to replace with something loud, but
> very safe. Since there is no guarantee that caller will ignore an error
> and try to dereference bvec the only safe thing we can to is to clamp
> iterator to zero to prevent any possible usage in future.

Since you prevent the case from happening, no dereference invalid bvec
can happen any more, but may cause dead loop. Setting iterator size as
zero can break the dead loop, but the driver still may not know this error
and continue to do its following work.

Don't have a better idea now, and looks it is fine to set iter.bi_size as
zero at the beginning of guarding bvec iteration.



Thanks,
Ming Lei

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web