Skip to content

Commit 386adb9

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 343e91b commit 386adb9

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
@@ -7762,6 +7762,18 @@ bool Item::cache_const_expr_analyzer(uchar **arg) {
77627762
return false;
77637763
}
77647764

7765+
bool Item::clean_up_after_removal(uchar *arg) {
7766+
Cleanup_after_removal_context *const ctx =
7767+
pointer_cast<Cleanup_after_removal_context *>(arg);
7768+
7769+
if (ctx->is_stopped(this)) return false;
7770+
7771+
if (reference_count() > 1) {
7772+
(void)decrement_ref_count();
7773+
}
7774+
return false;
7775+
}
7776+
77657777
bool Item::can_be_substituted_for_gc(bool array) const {
77667778
switch (real_item()->type()) {
77677779
case FUNC_ITEM:
@@ -8180,16 +8192,19 @@ bool Item_ref::clean_up_after_removal(uchar *arg) {
81808192

81818193
if (ctx->is_stopped(this)) return false;
81828194

8183-
// Exit if second visit to this object:
8184-
if (m_unlinked) return false;
8195+
// Decrement reference count for referencing object before
8196+
// referenced object:
8197+
if (reference_count() > 1) {
8198+
(void)decrement_ref_count();
8199+
ctx->stop_at(this);
8200+
return false;
8201+
}
8202+
if (ref_item()->is_abandoned()) return false;
81858203

81868204
if (ref_item()->decrement_ref_count() > 0) {
81878205
ctx->stop_at(this);
81888206
}
81898207

8190-
// Ensure the count is not decremented twice:
8191-
m_unlinked = true;
8192-
81938208
return false;
81948209
}
81958210

sql/item.h

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2878,6 +2878,7 @@ class Item : public Parse_tree_node {
28782878
*/
28792879
Query_block *const m_root;
28802880

2881+
friend class Item;
28812882
friend class Item_sum;
28822883
friend class Item_subselect;
28832884
friend class Item_ref;
@@ -2886,11 +2887,12 @@ class Item : public Parse_tree_node {
28862887
Clean up after removing the item from the item tree.
28872888
28882889
param arg pointer to a Cleanup_after_removal_context object
2890+
@todo: If class ORDER is refactored so that all indirect
2891+
grouping/ordering expressions are represented with Item_ref
2892+
objects, all implementations of cleanup_after_removal() except
2893+
the one for Item_ref can be removed.
28892894
*/
2890-
virtual bool clean_up_after_removal(uchar *arg [[maybe_unused]]) {
2891-
assert(arg != nullptr);
2892-
return false;
2893-
}
2895+
virtual bool clean_up_after_removal(uchar *arg);
28942896

28952897
/// @see Distinct_check::check_query()
28962898
virtual bool aggregate_check_distinct(uchar *) { return false; }
@@ -3348,6 +3350,9 @@ class Item : public Parse_tree_node {
33483350
*/
33493351
bool is_blob_field() const;
33503352

3353+
/// @returns number of references to an item.
3354+
uint reference_count() const { return m_ref_count; }
3355+
33513356
/// Increment reference count
33523357
void increment_ref_count() {
33533358
assert(!m_abandoned);
@@ -3493,6 +3498,8 @@ class Item : public Parse_tree_node {
34933498
*/
34943499
virtual void compute_cost(CostOfItem *root_cost [[maybe_unused]]) const {}
34953500

3501+
bool is_abandoned() const { return m_abandoned; }
3502+
34963503
private:
34973504
virtual bool subq_opt_away_processor(uchar *) { return false; }
34983505

@@ -5889,9 +5896,6 @@ class Item_ref : public Item_ident {
58895896
bool pusheddown_depended_from{false};
58905897

58915898
private:
5892-
/// True if referenced item has been unlinked, used during item tree removal
5893-
bool m_unlinked{false};
5894-
58955899
Field *result_field{nullptr}; /* Save result here */
58965900

58975901
protected:

sql/item_subselect.cc

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

2883+
if (reference_count() > 1) {
2884+
(void)decrement_ref_count();
2885+
ctx->stop_at(this);
2886+
return false;
2887+
}
2888+
28832889
// Remove item on upward traversal, not downward:
28842890
if (marker == MARKER_NONE) {
28852891
marker = MARKER_TRAVERSAL;

sql/item_sum.cc

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

531531
if (ctx->is_stopped(this)) return false;
532532

533+
if (reference_count() > 1) {
534+
(void)decrement_ref_count();
535+
ctx->stop_at(this);
536+
return false;
537+
}
538+
533539
// Remove item on upward traversal, not downward:
534540
if (marker == MARKER_NONE) {
535541
marker = MARKER_TRAVERSAL;

0 commit comments

Comments
 (0)