Commit message: MDEV-26402: A SEGV in Item_field::used_tables/update_depend_map_for_order...

When doing condition pushdown from HAVING into WHERE,
Item_equal::create_pushable_equalities() calls
item->set_extraction_flag(IMMUTABLE_FL) for constant items.
Then, Item::cleanup_excluding_immutables_processor() checks for this flag
to see if it should call item->cleanup() or leave the item as-is.

The failure happens when a constant item has a non-constant one inside it,
like:

   (tbl.col=0 AND impossible_cond)

item->walk(cleanup_excluding_immutables_processor) works in a bottom-up
way so it
1. will call Item_func_eq(tbl.col=0)->cleanup()
2. will not call Item_cond_and->cleanup (as the AND is constant)

This creates an item tree where a fixed Item has an un-fixed Item inside
it which eventually causes an assertion failure.

Fixed by introducing this rule: instead of just calling

  item->set_extraction_flag(IMMUTABLE_FL);

we call Item::walk() to set the flag for all sub-items of the item.
CVE: CVE-2022-27382
CWE: CWE-617
Vulnerability description: MariaDB Server v10.7 and below was discovered to contain a segmentation fault via the component Item_field::used_tables/update_depend_map_for_order.

Project: server

bool Item_equal::create_pushable_equalities(THD *thd,
                                            List<Item> *equalities,
                                            Pushdown_checker checker,
                                            uchar *arg,
                                            bool clone_const)
{
  Item *item;
  Item *left_item= NULL;
  Item *right_item = get_const();
  Item_equal_fields_iterator it(*this);

  while ((item=it++))
  {
    left_item= item;
    if (checker && !((item->*checker) (arg)))
      continue;
    break;
  }

  if (!left_item)
    return false;

  if (right_item)
  {
    Item_func_eq *eq= 0;
    Item *left_item_clone= left_item->build_clone(thd);
    Item *right_item_clone= !clone_const ?
                            right_item : right_item->build_clone(thd);
    if (!left_item_clone || !right_item_clone)
      return true;
    eq= new (thd->mem_root) Item_func_eq(thd,
                                         left_item_clone,
                                         right_item_clone);
    if (!eq ||  equalities->push_back(eq, thd->mem_root))
      return true;
    if (!clone_const)
      right_item->set_extraction_flag(IMMUTABLE_FL);
  }

  while ((item=it++))
  {
    if (checker && !((item->*checker) (arg)))
      continue;
    Item_func_eq *eq= 0;
    Item *left_item_clone= left_item->build_clone(thd);
    Item *right_item_clone= item->build_clone(thd);
    if (!(left_item_clone && right_item_clone))
      return true;
    left_item_clone->set_item_equal(NULL);
    right_item_clone->set_item_equal(NULL);
    eq= new (thd->mem_root) Item_func_eq(thd,
                                         right_item_clone,
                                         left_item_clone);
    if (!eq || equalities->push_back(eq, thd->mem_root))
      return true;
  }
  return false;
}

Project: server

bool Item_equal::create_pushable_equalities(THD *thd,
                                            List<Item> *equalities,
                                            Pushdown_checker checker,
                                            uchar *arg,
                                            bool clone_const)
{
  Item *item;
  Item *left_item= NULL;
  Item *right_item = get_const();
  Item_equal_fields_iterator it(*this);

  while ((item=it++))
  {
    left_item= item;
    if (checker && !((item->*checker) (arg)))
      continue;
    break;
  }

  if (!left_item)
    return false;

  if (right_item)
  {
    Item_func_eq *eq= 0;
    Item *left_item_clone= left_item->build_clone(thd);
    Item *right_item_clone= !clone_const ?
                            right_item : right_item->build_clone(thd);
    if (!left_item_clone || !right_item_clone)
      return true;
    eq= new (thd->mem_root) Item_func_eq(thd,
                                         left_item_clone,
                                         right_item_clone);
    if (!eq ||  equalities->push_back(eq, thd->mem_root))
      return true;
    if (!clone_const)
    {
      /*
        Also set IMMUTABLE_FL for any sub-items of the right_item.
        This is needed to prevent Item::cleanup_excluding_immutables_processor
        from peforming cleanup of the sub-items and so creating an item tree
        where a fixed item has non-fixed items inside it.
      */
      int new_flag= IMMUTABLE_FL;
      right_item->walk(&Item::set_extraction_flag_processor, false,
                       (void*)&new_flag);
    }
  }

  while ((item=it++))
  {
    if (checker && !((item->*checker) (arg)))
      continue;
    Item_func_eq *eq= 0;
    Item *left_item_clone= left_item->build_clone(thd);
    Item *right_item_clone= item->build_clone(thd);
    if (!(left_item_clone && right_item_clone))
      return true;
    left_item_clone->set_item_equal(NULL);
    right_item_clone->set_item_equal(NULL);
    eq= new (thd->mem_root) Item_func_eq(thd,
                                         right_item_clone,
                                         left_item_clone);
    if (!eq || equalities->push_back(eq, thd->mem_root))
      return true;
  }
  return false;
}