Lets start with where we left of earlier.
Experiment 1: Follow table_list→handle_derived(lex, DT_PREPARE) for view and derived table
FACT: views are already merged prior to mysql_derived_prepare() and 'merged_for_insert' is set. 'merged_underlying_list' is set during mysql_make_view() which has a call stack: open_tables()→open_tables()→open_and_process_table()→open_table→mysql_make_view()
Experiment 2: How is merge_underlying_list is created inside mysql_make_view
merge_underlying_list is used only by views but will be required to be used by derived as well to update. If we set merge_underlying_list for derived table then it completes our merging process.
For our simple case: update (select a from t1) d set a=0; merge_underyling_list (mul) will include only table t1.
Changes made:

Now, though we have fixed crash in storage engine but we are not back at crash happening due to same reasons as for the case 'update v1 set s2=s2+1' in view.test file i.e assertion failure at ha_update_row.
Above changes are the proof of concept that what changes will be needed. This change was only for given simple case but slowly other cases will be added.
Lets look at why it failed.
Experiment 3: Investigate why ha_update_row fails.
Assertion failing:
DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || 6998 m_lock_type == F_WRLCK);
Call stack: mysql_update→ha_update_row
F_WRLCK defined as 1 but m_lock_type has value 0.
sql_base.cc line 2091: table->reginfo.lock_type=TL_READ; /* Assume read */
m_lock_type is set in handler::ha_external_lock() with call stack:
mysql_update→lock_tables→mysql_lock_tables→mysql_lock_tables→lock_external→ha_external_lock(thd, lock_type)

Under open_tables