Skip to content

Commit 649c132

Browse files
Chaithra Gopalareddydahlerlend
authored andcommitted
Bug#35738548: SEGV (Item_ref::real_item() at sql/item.h:5825)
found by fuzzing tool Bug#35779012: SEGV (Item_subselect::print() at sql/item_subselect.cc:835) found by fuzzing tool Bug#35733778: SEGV (Item_subselect::exec() at sql/item_subselect.cc:660) found by fuzzing tool Bug#35738531: Another SEGV (Item_subselect::exec() at sql/item_subselect.cc:660) found by fuzzing tool If an Item_ref object is referenced from multiple places in a query block, and if the item that it refers to is also referenced from multiple places, there is a chance that while removing redundant expressions, we could end up removing the referenced item even though it is still being referred to. E.g. while resolving ORDER BY clause, if the expression is found in the select list, expression used in order by is removed and it starts using the one in select list. When this happens, while removing the redundant expressions from the query block, if the select expression is an Item_ref object, on the first visit to this expression, we mark the object as unlinked. On the second visit, this time because of the order by, as the object is marked as unlinked, it exits the traversal without doing anything. However, when the item it refers to is visited, it does not know that the item is still being referred to. So it ends up deleting the referenced item. Solution is to decrement the ref_count of an item without initiating the clean up of the underlying item unless its the last reference (This necessitated changes to all implementations of clean_up_after_removal). Along with this we also remove m_unlinked member because it's no more needed. If the underlying item of an Item_ref object is not abandoned, we decrement the ref count and stop looking further. Change-Id: I4ef3aaf92a8c0961a541dae09c766929d93bb64e
1 parent ae49139 commit 649c132

File tree

4 files changed

+43
-12
lines changed

4 files changed

+43
-12
lines changed

sql/item.cc

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7562,6 +7562,18 @@ bool Item::cache_const_expr_analyzer(uchar **arg) {
75627562
return false;
75637563
}
75647564

7565+
bool Item::clean_up_after_removal(uchar *arg) {
7566+
Cleanup_after_removal_context *const ctx =
7567+
pointer_cast<Cleanup_after_removal_context *>(arg);
7568+
7569+
if (ctx->is_stopped(this)) return false;
7570+
7571+
if (reference_count() > 1) {
7572+
(void)decrement_ref_count();
7573+
}
7574+
return false;
7575+
}
7576+
75657577
bool Item::can_be_substituted_for_gc(bool array) const {
75667578
switch (real_item()->type()) {
75677579
case FUNC_ITEM:
@@ -7977,16 +7989,19 @@ bool Item_ref::clean_up_after_removal(uchar *arg) {
79777989

79787990
if (ctx->is_stopped(this)) return false;
79797991

7980-
// Exit if second visit to this object:
7981-
if (m_unlinked) return false;
7992+
// Decrement reference count for referencing object before
7993+
// referenced object:
7994+
if (reference_count() > 1) {
7995+
(void)decrement_ref_count();
7996+
ctx->stop_at(this);
7997+
return false;
7998+
}
7999+
if (ref_item()->is_abandoned()) return false;
79828000

79838001
if (ref_item()->decrement_ref_count() > 0) {
79848002
ctx->stop_at(this);
79858003
}
79868004

7987-
// Ensure the count is not decremented twice:
7988-
m_unlinked = true;
7989-
79908005
return false;
79918006
}
79928007

sql/item.h

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2732,6 +2732,7 @@ class Item : public Parse_tree_node {
27322732
*/
27332733
Query_block *const m_root;
27342734

2735+
friend class Item;
27352736
friend class Item_sum;
27362737
friend class Item_subselect;
27372738
friend class Item_ref;
@@ -2740,11 +2741,12 @@ class Item : public Parse_tree_node {
27402741
Clean up after removing the item from the item tree.
27412742
27422743
param arg pointer to a Cleanup_after_removal_context object
2744+
@todo: If class ORDER is refactored so that all indirect
2745+
grouping/ordering expressions are represented with Item_ref
2746+
objects, all implementations of cleanup_after_removal() except
2747+
the one for Item_ref can be removed.
27432748
*/
2744-
virtual bool clean_up_after_removal(uchar *arg [[maybe_unused]]) {
2745-
assert(arg != nullptr);
2746-
return false;
2747-
}
2749+
virtual bool clean_up_after_removal(uchar *arg);
27482750

27492751
/// @see Distinct_check::check_query()
27502752
virtual bool aggregate_check_distinct(uchar *) { return false; }
@@ -3209,6 +3211,9 @@ class Item : public Parse_tree_node {
32093211
*/
32103212
bool is_blob_field() const;
32113213

3214+
/// @returns number of references to an item.
3215+
uint reference_count() const { return m_ref_count; }
3216+
32123217
/// Increment reference count
32133218
void increment_ref_count() {
32143219
assert(!m_abandoned);
@@ -3338,6 +3343,8 @@ class Item : public Parse_tree_node {
33383343
}
33393344
virtual bool strip_db_table_name_processor(uchar *) { return false; }
33403345

3346+
bool is_abandoned() const { return m_abandoned; }
3347+
33413348
private:
33423349
virtual bool subq_opt_away_processor(uchar *) { return false; }
33433350

@@ -5712,9 +5719,6 @@ class Item_ref : public Item_ident {
57125719
bool pusheddown_depended_from{false};
57135720

57145721
private:
5715-
/// True if referenced item has been unlinked, used during item tree removal
5716-
bool m_unlinked{false};
5717-
57185722
Field *result_field{nullptr}; /* Save result here */
57195723

57205724
protected:

sql/item_subselect.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2683,6 +2683,12 @@ bool Item_subselect::clean_up_after_removal(uchar *arg) {
26832683
// Check whether this item should be removed
26842684
if (ctx->is_stopped(this)) return false;
26852685

2686+
if (reference_count() > 1) {
2687+
(void)decrement_ref_count();
2688+
ctx->stop_at(this);
2689+
return false;
2690+
}
2691+
26862692
// Remove item on upward traversal, not downward:
26872693
if (marker == MARKER_NONE) {
26882694
marker = MARKER_TRAVERSAL;

sql/item_sum.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,12 @@ bool Item_sum::clean_up_after_removal(uchar *arg) {
524524

525525
if (ctx->is_stopped(this)) return false;
526526

527+
if (reference_count() > 1) {
528+
(void)decrement_ref_count();
529+
ctx->stop_at(this);
530+
return false;
531+
}
532+
527533
// Remove item on upward traversal, not downward:
528534
if (marker == MARKER_NONE) {
529535
marker = MARKER_TRAVERSAL;

0 commit comments

Comments
 (0)