* [PATCH 1/2] btree: Fix tree corruption in btree_get_prev() @ 2012-06-06 17:17 Roland Dreier 2012-06-06 17:17 ` [PATCH 2/2] btree: Catch NULL value before it does harm Roland Dreier 2012-06-06 23:21 ` [PATCH 1/2] btree: Fix tree corruption in btree_get_prev() Andrew Morton 0 siblings, 2 replies; 6+ messages in thread From: Roland Dreier @ 2012-06-06 17:17 UTC (permalink / raw) To: Andrew Morton; +Cc: Joern Engel, linux-kernel From: Roland Dreier <roland@purestorage.com> The memory the parameter __key points to is used as an iterator in btree_get_prev(), so if we save off a bkey() pointer in retry_key and then assign that to __key, we'll end up corrupting the btree internals when we do eg longcpy(__key, bkey(geo, node, i), geo->keylen); to return the key value. What we should do instead is use longcpy() to copy the key value that retry_key points to __key. This can cause a btree to get corrupted by seemingly read-only operations such as btree_for_each_safe. Acked-by: Joern Engel <joern@logfs.org> Cc: <stable@vger.kernel.org> Signed-off-by: Roland Dreier <roland@purestorage.com> --- lib/btree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/btree.c b/lib/btree.c index e5ec1e9..b6e889b 100644 --- a/lib/btree.c +++ b/lib/btree.c @@ -351,7 +351,7 @@ retry: } miss: if (retry_key) { - __key = retry_key; + longcpy(__key, retry_key, geo->keylen); retry_key = NULL; goto retry; } -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 2/2] btree: Catch NULL value before it does harm 2012-06-06 17:17 [PATCH 1/2] btree: Fix tree corruption in btree_get_prev() Roland Dreier @ 2012-06-06 17:17 ` Roland Dreier 2012-06-06 23:21 ` [PATCH 1/2] btree: Fix tree corruption in btree_get_prev() Andrew Morton 1 sibling, 0 replies; 6+ messages in thread From: Roland Dreier @ 2012-06-06 17:17 UTC (permalink / raw) To: Andrew Morton; +Cc: Joern Engel, linux-kernel From: Joern Engel <joern@logfs.org> Storing NULL values in the btree is illegal and can lead to memory corruption and possible other fun as well. Catch it on insert, instead of waiting for the inevitable. Signed-off-by: Joern Engel <joern@logfs.org> Signed-off-by: Roland Dreier <roland@purestorage.com> --- lib/btree.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/btree.c b/lib/btree.c index b6e889b..bd6d4b4 100644 --- a/lib/btree.c +++ b/lib/btree.c @@ -509,6 +509,7 @@ retry: int btree_insert(struct btree_head *head, struct btree_geo *geo, unsigned long *key, void *val, gfp_t gfp) { + BUG_ON(!val); return btree_insert_level(head, geo, key, val, 1, gfp); } EXPORT_SYMBOL_GPL(btree_insert); -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH 1/2] btree: Fix tree corruption in btree_get_prev() 2012-06-06 17:17 [PATCH 1/2] btree: Fix tree corruption in btree_get_prev() Roland Dreier 2012-06-06 17:17 ` [PATCH 2/2] btree: Catch NULL value before it does harm Roland Dreier @ 2012-06-06 23:21 ` Andrew Morton 2012-06-06 23:36 ` Roland Dreier 1 sibling, 1 reply; 6+ messages in thread From: Andrew Morton @ 2012-06-06 23:21 UTC (permalink / raw) To: Roland Dreier; +Cc: Joern Engel, linux-kernel On Wed, 6 Jun 2012 10:17:26 -0700 Roland Dreier <roland@kernel.org> wrote: > From: Roland Dreier <roland@purestorage.com> > > The memory the parameter __key points to is used as an iterator in > btree_get_prev(), so if we save off a bkey() pointer in retry_key and > then assign that to __key, we'll end up corrupting the btree internals > when we do eg > > longcpy(__key, bkey(geo, node, i), geo->keylen); > > to return the key value. What we should do instead is use longcpy() to > copy the key value that retry_key points to __key. > > This can cause a btree to get corrupted by seemingly read-only > operations such as btree_for_each_safe. > > Acked-by: Joern Engel <joern@logfs.org> > Cc: <stable@vger.kernel.org> > Signed-off-by: Roland Dreier <roland@purestorage.com> > --- > lib/btree.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/lib/btree.c b/lib/btree.c > index e5ec1e9..b6e889b 100644 > --- a/lib/btree.c > +++ b/lib/btree.c > @@ -351,7 +351,7 @@ retry: > } > miss: > if (retry_key) { > - __key = retry_key; > + longcpy(__key, retry_key, geo->keylen); > retry_key = NULL; > goto retry; > } Fair enough. I think we can do this to save a few cycles? --- a/lib/btree.c~btree-fix-tree-corruption-in-btree_get_prev-fix +++ a/lib/btree.c @@ -319,8 +319,8 @@ void *btree_get_prev(struct btree_head * if (head->height == 0) return NULL; -retry: longcpy(key, __key, geo->keylen); +retry: dec_key(geo, key); node = head->node; @@ -351,7 +351,7 @@ retry: } miss: if (retry_key) { - longcpy(__key, retry_key, geo->keylen); + longcpy(key, retry_key, geo->keylen); retry_key = NULL; goto retry; } _ ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 1/2] btree: Fix tree corruption in btree_get_prev() 2012-06-06 23:21 ` [PATCH 1/2] btree: Fix tree corruption in btree_get_prev() Andrew Morton @ 2012-06-06 23:36 ` Roland Dreier 2012-06-07 0:44 ` Roland Dreier 0 siblings, 1 reply; 6+ messages in thread From: Roland Dreier @ 2012-06-06 23:36 UTC (permalink / raw) To: Andrew Morton; +Cc: Joern Engel, linux-kernel On Wed, Jun 6, 2012 at 4:21 PM, Andrew Morton <akpm@linux-foundation.org> wrote: > I think we can do this to save a few cycles? > > --- a/lib/btree.c~btree-fix-tree-corruption-in-btree_get_prev-fix > +++ a/lib/btree.c > @@ -319,8 +319,8 @@ void *btree_get_prev(struct btree_head * > > if (head->height == 0) > return NULL; > -retry: > longcpy(key, __key, geo->keylen); > +retry: > dec_key(geo, key); > > node = head->node; > @@ -351,7 +351,7 @@ retry: > } > miss: > if (retry_key) { > - longcpy(__key, retry_key, geo->keylen); > + longcpy(key, retry_key, geo->keylen); > retry_key = NULL; > goto retry; > } Looks reasonable to me, and passes my hacky userspace test harness. On the other hand, your change makes me think we don't even need a separate iterator (and we can avoid the variable length array declaration), ie we could do --- a/lib/btree.c +++ b/lib/btree.c @@ -312,7 +312,7 @@ void *btree_get_prev(struct btree_head *head, struct btree_geo *geo, { int i, height; unsigned long *node, *oldnode; - unsigned long *retry_key = NULL, key[geo->keylen]; + unsigned long *retry_key = NULL; if (keyzero(geo, __key)) return NULL; @@ -320,13 +320,12 @@ void *btree_get_prev(struct btree_head *head, struct btree_geo *geo, if (head->height == 0) return NULL; retry: - longcpy(key, __key, geo->keylen); - dec_key(geo, key); + dec_key(geo, __key); node = head->node; for (height = head->height ; height > 1; height--) { for (i = 0; i < geo->no_pairs; i++) - if (keycmp(geo, node, i, key) <= 0) + if (keycmp(geo, node, i, __key) <= 0) break; if (i == geo->no_pairs) goto miss; @@ -341,7 +340,7 @@ retry: goto miss; for (i = 0; i < geo->no_pairs; i++) { - if (keycmp(geo, node, i, key) <= 0) { + if (keycmp(geo, node, i, __key) <= 0) { if (bval(geo, node, i)) { longcpy(__key, bkey(geo, node, i), geo->keylen); return bval(geo, node, i); but this means even if we fail and return NULL we mess with the caller's __key. So your way may be better. Joern? - R. ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 1/2] btree: Fix tree corruption in btree_get_prev() 2012-06-06 23:36 ` Roland Dreier @ 2012-06-07 0:44 ` Roland Dreier 2012-06-07 1:29 ` Andrew Morton 0 siblings, 1 reply; 6+ messages in thread From: Roland Dreier @ 2012-06-07 0:44 UTC (permalink / raw) To: Andrew Morton; +Cc: Joern Engel, linux-kernel On Wed, Jun 6, 2012 at 4:36 PM, Roland Dreier <roland@kernel.org> wrote: > On the other hand, your change makes me think we don't > even need a separate iterator (and we can avoid the variable > length array declaration) FWIW with that change on top of my patch, I see add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-123 (-123) function old new delta btree_get_prev 646 523 -123 on x86-64, so avoiding the variable length array is definitely worth something. So the issue for me is whether messing with the caller's __key storage is OK, or if it's worth having a temporary local variable. - R. ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 1/2] btree: Fix tree corruption in btree_get_prev() 2012-06-07 0:44 ` Roland Dreier @ 2012-06-07 1:29 ` Andrew Morton 0 siblings, 0 replies; 6+ messages in thread From: Andrew Morton @ 2012-06-07 1:29 UTC (permalink / raw) To: Roland Dreier; +Cc: Joern Engel, linux-kernel On Wed, 6 Jun 2012 17:44:20 -0700 Roland Dreier <roland@kernel.org> wrote: > On Wed, Jun 6, 2012 at 4:36 PM, Roland Dreier <roland@kernel.org> wrote: > > On the other hand, your change makes me think we don't > > even need a separate iterator (and we can avoid the variable > > length array declaration) > > FWIW with that change on top of my patch, I see > > add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-123 (-123) > function old new delta > btree_get_prev 646 523 -123 > > on x86-64, so avoiding the variable length array is definitely > worth something. > > So the issue for me is whether messing with the caller's > __key storage is OK, or if it's worth having a temporary > local variable. > Sometimes altering the caller's *__key when lookup fails is pretty rude behavior :( Perhaps we could add an arg to btree_get_prev(), provide it with separate input and output key pointers? ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2012-06-07 1:29 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2012-06-06 17:17 [PATCH 1/2] btree: Fix tree corruption in btree_get_prev() Roland Dreier 2012-06-06 17:17 ` [PATCH 2/2] btree: Catch NULL value before it does harm Roland Dreier 2012-06-06 23:21 ` [PATCH 1/2] btree: Fix tree corruption in btree_get_prev() Andrew Morton 2012-06-06 23:36 ` Roland Dreier 2012-06-07 0:44 ` Roland Dreier 2012-06-07 1:29 ` Andrew Morton
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox