Skip to content

[fix](be) Own the allocated LSN map in SharedMemtable to fix UAF on flush task teardown - #67442

Open
bosswnx wants to merge 1 commit into
apache:masterfrom
bosswnx:fix/uaf-shared-memtable-lsn-map-67428
Open

[fix](be) Own the allocated LSN map in SharedMemtable to fix UAF on flush task teardown#67442
bosswnx wants to merge 1 commit into
apache:masterfrom
bosswnx:fix/uaf-shared-memtable-lsn-map-67428

Conversation

@bosswnx

@bosswnx bosswnx commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: close #67428

Related PR: #66889 (introduced the UAF)

Problem Summary:

ASAN heap-use-after-free in SharedMemtable::~SharedMemtable() during group-commit memtable flush task teardown, introduced by #66889. PartOfGroupMemtableFlushTask holds its SharedMemtable by shared_ptr but only a weak_ptr to the FlushToken. When run()'s local shared_ptr<FlushToken> drops the last reference at the end of run(), destruction cascades FlushToken -> RowsetWriter -> RowsetWriterContext; the thread pool then destroys the task object, and ~SharedMemtable() dereferences the dangling raw RowsetWriterContext* rowset_ctx in remove_segment_allocated_lsns(). Reproduced by the nonConcurrent pipeline on two unrelated PRs (#67404, #67402).

What changed?

  • SharedMemtable now owns std::shared_ptr<SegmentAllocatedLsnMap> captured from the group writer context at submission; insert/remove go through the owned map. A null map stands for "no LSN allocation" (equivalent to need_allocated_lsn(), since GroupRowsetWriter::init() creates the map exactly when needed). This keeps the precise cleanup dependency alive without extending the whole RowsetWriter lifetime — the approach recommended in the [Bug] BE crash (ASAN heap-use-after-free) in SharedMemtable dtor during group commit flush #67428 triage.
  • Add SegmentAllocatedLsnMap::contains_segment() for test assertions.
  • Regression tests covering: (a) the last token/writer owner released while a group flush task finishes, (b) a queued subtask running after its weak token expired, (c) cancellation, plus LSN-entry cleanup assertions on the flush-error path.

Verification

ASAN BE UT, both directions: without the fix the new tests abort with the exact reported UAF (SUMMARY: AddressSanitizer: heap-use-after-free rowset_writer_context.h:202:9 in doris::RowsetWriterContext::remove_segment_allocated_lsns); with the fix the MemTableFlushExecutor* tests pass 8/8 (two runs).

Release note

None

Check List (For Author)

  • Test

    • Regression test
    • Unit Test
    • Manual test (add detailed scripts or steps below)
    • No need to test or manual test. Explain why:
      • This is a refactor/code format and no logic has been changed.
      • Previous test can cover this change.
      • No code files have been changed.
      • Other reason
  • Behavior changed:

    • No.
    • Yes.
  • Does this need documentation?

    • No.
    • Yes.

Check List (For Reviewer who merge this PR)

  • Confirm the release note
  • Confirm test cases
  • Confirm document
  • Add branch pick label

…lush task teardown

PartOfGroupMemtableFlushTask holds its SharedMemtable by shared_ptr but
only a weak_ptr to the FlushToken. When run()'s local shared_ptr<FlushToken>
drops the last reference at the end of run(), destruction cascades
FlushToken -> RowsetWriter -> RowsetWriterContext, while the thread pool
destroys the task object right after; ~SharedMemtable() then dereferences
the raw RowsetWriterContext* rowset_ctx in remove_segment_allocated_lsns(),
reading freed memory (ASAN heap-use-after-free, issue apache#67428, reproduced
by the nonConcurrent pipeline on PRs apache#67404 and apache#67402).

Fix: SharedMemtable now owns the shared_ptr<SegmentAllocatedLsnMap>
captured from the group writer context at submission, and inserts/removes
LSN entries through that map. A null map stands for "no LSN allocation",
which is equivalent to RowsetWriterContext::need_allocated_lsn() because
GroupRowsetWriter::init() creates the map exactly when LSN allocation is
required. This keeps the precise cleanup dependency alive for the task
teardown without extending the whole RowsetWriter lifetime.

Also add SegmentAllocatedLsnMap::contains_segment() for test assertions,
and regression tests covering: (a) the last token/writer owner released
while a group flush task finishes, (b) a queued subtask running after its
weak token expired, (c) cancellation, plus LSN-entry cleanup assertions on
the flush-error path. Verified with and without the fix under ASAN: the
new tests reproduce the exact use-after-free at
RowsetWriterContext::remove_segment_allocated_lsns without the fix and
pass with it.
@hello-stephen

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Apache Doris.
Don't know what should be done next? See How to process your PR.

Please clearly describe your PR:

  1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
  2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
  3. What features were added. Why was this function added?
  4. Which code was refactored and why was this part of the code refactored?
  5. Which functions were optimized and what is the difference before and after the optimization?

@bosswnx

bosswnx commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 16915 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit b63e944be1957bdd909ba34ae87dfa3672d14c14, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17587	3023	3042	3023
q2	2109	258	220	220
q3	10221	892	507	507
q4	4675	243	202	202
q5	7680	582	386	386
q6	140	114	94	94
q7	522	507	391	391
q8	9236	942	928	928
q9	3492	2373	2383	2373
q10	6526	848	718	718
q11	393	193	180	180
q12	623	252	203	203
q13	18130	1531	1150	1150
q14	162	153	142	142
q15	q16	445	397	371	371
q17	1404	815	836	815
q18	3065	2239	2243	2239
q19	1282	870	739	739
q20	391	284	200	200
q21	5596	1808	1859	1808
q22	327	271	226	226
Total cold run time: 94006 ms
Total hot run time: 16915 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	3431	3351	3323	3323
q2	508	398	368	368
q3	2212	2303	2117	2117
q4	1183	1166	901	901
q5	2181	2103	2125	2103
q6	165	122	90	90
q7	1024	948	847	847
q8	1600	1411	1413	1411
q9	3120	3112	3102	3102
q10	1855	1792	1613	1613
q11	364	271	249	249
q12	449	425	346	346
q13	1461	1517	1145	1145
q14	185	173	167	167
q15	q16	385	393	357	357
q17	3691	3337	3221	3221
q18	4781	4412	4698	4412
q19	863	913	898	898
q20	999	969	812	812
q21	3859	3171	3157	3157
q22	384	357	314	314
Total cold run time: 34700 ms
Total hot run time: 30953 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 82483 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit b63e944be1957bdd909ba34ae87dfa3672d14c14, data reload: false

query5	4260	397	340	340
query6	391	145	129	129
query7	4950	425	231	231
query8	292	127	133	127
query9	8711	2840	2867	2840
query10	403	238	183	183
query11	5369	1058	932	932
query12	118	68	68	68
query13	1204	426	327	327
query14	6117	2157	2064	2064
query14_1	1957	1941	1928	1928
query15	176	122	111	111
query16	941	369	360	360
query17	810	444	375	375
query18	2336	326	244	244
query19	168	145	116	116
query20	69	69	74	69
query21	198	101	90	90
query22	5534	5304	5388	5304
query23	6895	6192	6124	6124
query23_1	5949	6010	6083	6010
query24	7274	1105	790	790
query24_1	792	787	798	787
query25	442	310	275	275
query26	1241	242	137	137
query27	2755	393	250	250
query28	4728	1504	1505	1504
query29	940	444	368	368
query30	258	160	133	133
query31	823	404	339	339
query32	131	90	72	72
query33	466	227	194	194
query34	982	824	499	499
query35	424	421	347	347
query36	578	559	531	531
query37	122	82	75	75
query38	1017	854	820	820
query39	493	484	486	484
query39_1	453	497	478	478
query40	206	92	90	90
query41	59	57	56	56
query42	76	74	77	74
query43	239	237	218	218
query44	1027	558	576	558
query45	108	109	101	101
query46	774	838	530	530
query47	783	767	714	714
query48	320	313	235	235
query49	549	242	193	193
query50	778	268	206	206
query51	8269	8104	8113	8104
query52	75	67	61	61
query53	191	200	147	147
query54	228	167	176	167
query55	70	58	66	58
query56	312	182	170	170
query57	710	657	638	638
query58	195	166	156	156
query59	1205	1219	1107	1107
query60	228	187	178	178
query61	116	111	125	111
query62	353	202	187	187
query63	171	139	140	139
query64	2767	722	600	600
query65	1646	1615	1688	1615
query66	1877	250	200	200
query67	10023	9800	9600	9600
query68	3011	1247	737	737
query69	337	220	199	199
query70	678	617	636	617
query71	252	173	172	172
query72	2399	1720	1596	1596
query73	640	590	364	364
query74	2003	1214	1140	1140
query75	1166	1093	955	955
query76	2359	728	533	533
query77	243	261	221	221
query78	4032	3648	3273	3273
query79	2781	870	603	603
query80	1614	338	286	286
query81	514	157	131	131
query82	615	136	96	96
query83	284	208	188	188
query84	299	112	93	93
query85	840	364	304	304
query86	480	175	180	175
query87	1005	983	902	902
query88	3061	2126	2120	2120
query89	284	197	178	178
query90	2176	130	125	125
query91	133	113	105	105
query92	98	70	72	70
query93	2288	1115	687	687
query94	662	276	218	218
query95	508	320	230	230
query96	812	591	290	290
query97	1058	1053	986	986
query98	180	135	136	135
query99	417	353	307	307
Total cold run time: 180787 ms
Total hot run time: 82483 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 14.58 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit b63e944be1957bdd909ba34ae87dfa3672d14c14, data reload: false

query1	0.01	0.01	0.01
query2	0.08	0.04	0.03
query3	0.25	0.12	0.10
query4	1.60	0.09	0.10
query5	0.17	0.15	0.16
query6	1.27	0.73	0.69
query7	0.03	0.01	0.00
query8	0.05	0.03	0.03
query9	0.29	0.21	0.22
query10	0.34	0.36	0.34
query11	0.16	0.12	0.12
query12	0.14	0.12	0.11
query13	0.30	0.32	0.32
query14	0.45	0.44	0.44
query15	0.37	0.36	0.35
query16	0.20	0.24	0.22
query17	0.71	0.69	0.66
query18	0.18	0.15	0.16
query19	1.20	1.13	1.16
query20	0.01	0.01	0.01
query21	15.45	0.16	0.13
query22	5.03	0.04	0.05
query23	16.19	0.25	0.10
query24	3.06	0.33	0.27
query25	0.11	0.05	0.04
query26	0.70	0.16	0.11
query27	0.04	0.03	0.02
query28	3.71	0.54	0.25
query29	12.43	3.17	2.56
query30	0.25	0.12	0.13
query31	2.75	0.36	0.18
query32	3.56	0.31	0.23
query33	1.40	1.46	1.41
query34	15.39	2.16	1.74
query35	1.76	1.73	1.72
query36	0.46	0.28	0.29
query37	0.06	0.04	0.04
query38	0.05	0.04	0.03
query39	0.04	0.02	0.03
query40	0.11	0.07	0.08
query41	0.08	0.02	0.02
query42	0.04	0.02	0.02
query43	0.03	0.02	0.03
Total cold run time: 90.51 s
Total hot run time: 14.58 s

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 100% (0/0) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 76.22% (34681/45503)
Line Coverage 61.25% (391258/638804)
Region Coverage 57.46% (328939/572500)
Branch Coverage 58.23% (150017/257637)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] BE crash (ASAN heap-use-after-free) in SharedMemtable dtor during group commit flush

2 participants