Skip to content

[fix](audit) escape 0x1F/0x1E in audit_log stream load to prevent row forgery - #66580

Open
CalvinKirs wants to merge 1 commit into
apache:masterfrom
CalvinKirs:fix-o07-audit-log-injection
Open

[fix](audit) escape 0x1F/0x1E in audit_log stream load to prevent row forgery#66580
CalvinKirs wants to merge 1 commit into
apache:masterfrom
CalvinKirs:fix-o07-audit-log-injection

Conversation

@CalvinKirs

Copy link
Copy Markdown
Member

Proposed changes

The builtin audit plugin frames its stream-load payload for
__internal_schema.audit_log with 0x1F as the column separator and
0x1E as the row delimiter (see AuditLoader.AUDIT_TABLE_COL_SEPARATOR
/ AUDIT_TABLE_LINE_DELIMITER). In AuditLoader.fillLogBuffer, however,
the string columns — statement text, catalog/db, user, changed
variables, error message, workload group, etc. — were appended without
escaping.

Because these fields can carry user-controlled content (and a SQL
statement may legitimately contain arbitrary bytes inside a block
comment or string literal, which the lexer accepts), a crafted statement
containing raw 0x1F/0x1E could end its own audit row early and have
the trailing bytes parsed as an additional, fully attacker-controlled
row. This allows forging or misattributing rows in the audit table
(CWE-117 log injection).

Changes

  • Add sanitizeField() in AuditLoader, which replaces the two framing
    bytes (0x1F, 0x1E) with a space. Only these two bytes are
    structural, so all other content — including newlines and tabs already
    present in SQL text — is preserved unchanged.
  • Route every string column in fillLogBuffer through the new
    appendField() helper so that new string columns added in the future
    are covered automatically. Numeric and boolean columns are appended
    directly since they can never contain these bytes.
  • Add unit tests asserting that injected delimiters cannot add rows or
    columns, and that ordinary statements pass through unchanged.

The text-file audit sink (AuditLogBuilder, fe.audit.log) uses a
|key=value format and is unaffected.

Types of changes

  • Bugfix (non-breaking change which fixes an issue)

Further comments

Behavior-preserving: only the two structural bytes, which are not
meaningful data, are affected. Existing clusters and audit consumers are
unchanged.

@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?

… forgery

The audit plugin frames its stream-load payload for
__internal_schema.audit_log with 0x1F (column separator) and 0x1E (row
delimiter), but the string columns in AuditLoader.fillLogBuffer were
appended without escaping. A statement carrying raw 0x1F/0x1E bytes --
e.g. inside a block comment or string literal, which the lexer accepts
-- could therefore close its own audit row early and have the trailing
bytes parsed as an additional, fully attacker-controlled row, forging or
misattributing rows in the audit table (CWE-117 log injection).

- Add sanitizeField(), which replaces the two framing bytes (0x1F, 0x1E)
  with a space. Only these two bytes are structural, so all other
  content -- including newlines and tabs already present in SQL text --
  is preserved unchanged.
- Route every string column in fillLogBuffer through appendField() /
  appendLastField() so none can bypass the sanitizer and new string
  columns are covered automatically. Note that planTimesMs,
  getMetaTimesMs and scheduleTimesMs are String columns despite the Ms
  suffix. Numeric and boolean columns are appended directly since they
  can never contain these bytes.
- Add unit tests asserting that injected delimiters (in stmt, user, db
  and planTimesMs) cannot add rows or columns, and that ordinary
  statements pass through unchanged.

The text-file audit sink (AuditLogBuilder, fe.audit.log) uses a
|key=value format and is unaffected.
@CalvinKirs
CalvinKirs force-pushed the fix-o07-audit-log-injection branch from bed1d7c to 90bba2a Compare August 7, 2026 09:10
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

PR approved by at least one committer and no changes requested.

@github-actions github-actions Bot added approved Indicates a PR has been approved by one committer. reviewed labels Aug 7, 2026
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

PR approved by anyone and no changes requested.

@CalvinKirs

Copy link
Copy Markdown
Member Author

run buildall

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17779	4009	3996	3996
q2	2000	310	207	207
q3	10348	1405	820	820
q4	4680	475	340	340
q5	7502	844	578	578
q6	182	172	136	136
q7	739	919	593	593
q8	9334	1658	1613	1613
q9	5300	4144	4086	4086
q10	6734	1598	1356	1356
q11	517	367	322	322
q12	709	573	455	455
q13	18076	3255	2733	2733
q14	269	254	246	246
q15	q16	797	724	674	674
q17	1051	987	1045	987
q18	6604	5642	5537	5537
q19	1214	1213	1094	1094
q20	777	688	586	586
q21	6393	2858	2691	2691
q22	441	382	325	325
Total cold run time: 101446 ms
Total hot run time: 29375 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	5016	4602	4642	4602
q2	297	336	214	214
q3	4878	5250	4779	4779
q4	2190	2248	1426	1426
q5	4516	4582	4432	4432
q6	229	177	128	128
q7	1917	1729	1498	1498
q8	2376	2061	2035	2035
q9	7227	6750	6701	6701
q10	4267	4211	3872	3872
q11	515	371	340	340
q12	703	715	495	495
q13	2925	3256	2761	2761
q14	266	275	249	249
q15	q16	661	672	592	592
q17	1244	1213	1204	1204
q18	12145	11017	11821	11017
q19	1085	1079	1097	1079
q20	2179	2176	1916	1916
q21	5336	4515	4559	4515
q22	526	456	416	416
Total cold run time: 60498 ms
Total hot run time: 54271 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 158344 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 90bba2a1f9118d7962179d93392cd20c52bdf4cd, data reload: false

query5	4300	596	463	463
query6	480	218	210	210
query7	4912	558	319	319
query8	323	163	147	147
query9	8798	4077	4090	4077
query10	492	361	311	311
query11	5751	2183	1967	1967
query12	148	101	98	98
query13	1240	598	435	435
query14	6075	4259	4002	4002
query14_1	3825	3803	3879	3803
query15	209	196	177	177
query16	1007	474	435	435
query17	906	674	528	528
query18	2420	461	325	325
query19	201	182	150	150
query20	101	99	100	99
query21	231	164	131	131
query22	13138	13053	12798	12798
query23	15826	14937	14499	14499
query23_1	14618	14756	14667	14667
query24	7635	1728	1280	1280
query24_1	1243	1261	1269	1261
query25	526	412	351	351
query26	1316	342	221	221
query27	2622	562	383	383
query28	4554	2045	2032	2032
query29	1034	615	471	471
query30	341	262	226	226
query31	1186	1108	1059	1059
query32	105	61	64	61
query33	539	298	231	231
query34	1194	1091	655	655
query35	735	733	627	627
query36	767	777	664	664
query37	155	109	95	95
query38	1838	1787	1692	1692
query39	848	819	797	797
query39_1	802	802	798	798
query40	250	162	138	138
query41	66	64	62	62
query42	95	94	90	90
query43	319	325	275	275
query44	1413	758	766	758
query45	187	177	172	172
query46	1035	1152	726	726
query47	1545	1514	1411	1411
query48	414	429	294	294
query49	589	417	304	304
query50	1035	416	335	335
query51	10416	10445	10613	10445
query52	90	88	83	83
query53	258	280	206	206
query54	317	268	227	227
query55	78	70	66	66
query56	305	285	285	285
query57	1018	989	937	937
query58	287	280	248	248
query59	1554	1610	1367	1367
query60	315	272	269	269
query61	151	150	148	148
query62	403	320	267	267
query63	242	193	198	193
query64	2850	1032	864	864
query65	3844	3803	3786	3786
query66	1833	462	371	371
query67	20212	19829	25163	19829
query68	3338	1586	1039	1039
query69	408	314	274	274
query70	878	794	806	794
query71	399	345	349	345
query72	3198	2779	2458	2458
query73	821	791	449	449
query74	4636	4514	4333	4333
query75	2388	2343	2008	2008
query76	2338	1148	754	754
query77	352	368	285	285
query78	11181	11049	10643	10643
query79	1374	1089	765	765
query80	657	548	466	466
query81	451	328	290	290
query82	626	177	143	143
query83	410	331	297	297
query84	325	163	131	131
query85	933	601	506	506
query86	324	235	205	205
query87	1981	1973	1845	1845
query88	3706	2806	2796	2796
query89	399	324	274	274
query90	1935	195	199	195
query91	208	188	163	163
query92	62	58	55	55
query93	1568	1700	1012	1012
query94	563	366	305	305
query95	784	539	570	539
query96	1025	756	356	356
query97	2449	2474	2335	2335
query98	196	186	187	186
query99	735	734	610	610
Total cold run time: 244387 ms
Total hot run time: 158344 ms

@hello-stephen

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

query1	0.00	0.00	0.00
query2	0.09	0.05	0.05
query3	0.25	0.13	0.13
query4	1.60	0.15	0.14
query5	0.24	0.21	0.22
query6	1.16	0.82	0.80
query7	0.04	0.01	0.00
query8	0.05	0.04	0.04
query9	0.39	0.32	0.31
query10	0.59	0.58	0.53
query11	0.20	0.14	0.14
query12	0.18	0.14	0.15
query13	0.46	0.47	0.47
query14	0.99	0.99	0.98
query15	0.61	0.59	0.59
query16	0.32	0.33	0.32
query17	1.09	1.11	1.10
query18	0.21	0.19	0.20
query19	1.99	1.91	1.89
query20	0.01	0.02	0.01
query21	15.47	0.19	0.13
query22	4.92	0.05	0.05
query23	16.13	0.29	0.14
query24	2.97	0.43	0.31
query25	0.11	0.04	0.04
query26	0.73	0.21	0.14
query27	0.04	0.04	0.03
query28	3.63	0.80	0.32
query29	12.46	3.98	3.17
query30	0.27	0.16	0.15
query31	2.80	0.56	0.31
query32	3.22	0.59	0.49
query33	3.12	3.27	3.15
query34	15.75	3.93	3.25
query35	3.22	3.24	3.21
query36	0.55	0.46	0.43
query37	0.09	0.06	0.06
query38	0.06	0.04	0.04
query39	0.04	0.03	0.02
query40	0.17	0.14	0.14
query41	0.08	0.03	0.03
query42	0.04	0.04	0.03
query43	0.04	0.04	0.04
Total cold run time: 96.38 s
Total hot run time: 23.72 s

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 93.55% (29/31) 🎉
Increment coverage report
Complete coverage report

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

Labels

approved Indicates a PR has been approved by one committer. reviewed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants