Skip to content

Comments

Fix ASAN leaks in casted_clog_bytearray with stdout logging#5800

Open
Copilot wants to merge 6 commits intomainfrom
copilot/fix-asas-leaks-in-casted-clog-bytearray
Open

Fix ASAN leaks in casted_clog_bytearray with stdout logging#5800
Copilot wants to merge 6 commits intomainfrom
copilot/fix-asas-leaks-in-casted-clog-bytearray

Conversation

Copy link
Contributor

Copilot AI commented Feb 18, 2026

Description

Fixes memory leaks in casted_clog_bytearray when using stdout logging, caused by C's unspecified argument evaluation order.

The clog macro expands to:

struct clog_param * __head = 0;
clog_stdout(__head, fmt, casted_clog_bytearray(data, len, &__head));

casted_clog_bytearray updates __head via the &__head pointer, but if the compiler evaluates __head (first argument) before calling casted_clog_bytearray, clog_stdout receives NULL for head and never frees the allocated memory.

The fix changes clog_stdout to accept struct clog_param ** (double pointer) instead of struct clog_param *. The macro now passes &__head, and clog_stdout dereferences it after function entry (when all arguments are fully evaluated), ensuring it always sees the correct linked list head.

Changes:

  • src/inc/quic_trace.h: Updated both the extern declaration and stub inline definition of clog_stdout to use struct clog_param **. Updated the clog macro to pass &__head instead of __head. Added an inline comment explaining why the address-of pattern is necessary (the CASTED_CLOG_BYTEARRAY macro in __VA_ARGS__ hardcodes the __head variable name and modifies it during argument evaluation).
  • src/generated/stdout/quic_trace.c: Updated clog_stdout implementation to accept struct clog_param ** head_ptr and dereference it on entry (struct clog_param * head = *head_ptr;).

Testing

Build verified with pwsh ./scripts/build.ps1 -loggingType stdout -DisablePerf -DisableTools -Tls openssl -Clean -SanitizeAddress. All targets compiled and linked without errors. Existing tests cover this path, and the fix is verified by ASAN no longer reporting leaks from casted_clog_bytearray allocations.

Documentation

No documentation impact.

Original prompt

This section details on the original issue you should resolve

<issue_title>ASAN leaks in casted_clog_bytearray with stdout logging</issue_title>
<issue_description>### Describe the bug

All leaks are allocated in casted_clog_bytearray in quic_trace.c. This function allocates byte arrays for formatting trace output and they are supposed to be freed in function clog_stdout(struct clog_param * head, const char * const format, ...) before exting:

CXPLAT_FREE(head->str, QUIC_POOL_TMP_ALLOC);

The issue is that clog_stdout never gets the __head pointer value updated by casted_clog_bytearray. In the following code example:

struct clog_param * __head = 0;
clog_stdout(__head, fmt, casted_clog_bytearray(data, len, &__head));

casted_clog_bytearray updates __head via *head = param;during argument evaluation and the updated __head is expected to be passed into clog_stdout, but this depends on argument evaluation order. When clog_stdout arguments are evaluated from left to right, __head is evaluated before the casted_clog_bytearray() call, so clog_stdout gets NULL `head' and skipped its cleanup loop — leaking all memory allocated in casted_clog_bytearray.

The fix is to pass &__head to clog_stdout. Regardless of evaluation order, the value of this double pointer is the same. Inside clog_stdout, the pointer is dereferenced after function entry (when all arguments have been fully evaluated), so it always sees the correct linked list head in clog_stdout, thus will be able to free the memory properly.

The leaks stop happening with the attached fix:
fix-clog-stdout-memory-leak.patch

Sample ASAN reports:

Direct leak of 192 byte(s) in 12 object(s) allocated from:
    microsoft/msquic#0 0x58f5d92cc134 in malloc (/home/leikong/repo/meru-common/out/test/quic_transport.test/quic_transport.test+0x1c3134) (BuildId: 1aab74186928132fd9c6fed25feb4cbe93eed340)
    microsoft/msquic#1 0x7058b6a1a139 in CxPlatAlloc /home/leikong/repo/meru-common/ext/msquic/src/platform/platform_posix.c:285:12
    microsoft/msquic#2 0x7058b6a4c435 in casted_clog_bytearray /home/leikong/repo/meru-common/ext/msquic/src/generated/stdout/quic_trace.c:32:9
    microsoft/msquic#3 0x7058b6a407c6 in CxPlatSocketContextRecvComplete /home/leikong/repo/meru-common/ext/msquic/src/platform/datapath_epoll.c:1671:9
    microsoft/msquic#4 0x7058b6a41e5a in CxPlatSocketReceiveCoalesced /home/leikong/repo/meru-common/ext/msquic/src/platform/datapath_epoll.c:1805:9
    microsoft/msquic#5 0x7058b6a44075 in CxPlatSocketReceive /home/leikong/repo/meru-common/ext/msquic/src/platform/datapath_epoll.c:1984:13
    microsoft/msquic#6 0x7058b6a37b6e in CxPlatSocketContextIoEventComplete /home/leikong/repo/meru-common/ext/msquic/src/platform/datapath_epoll.c:2606:17
    microsoft/msquic#7 0x58f5d9bdfb9b in quicpp::execution::poll_loop_async(unsigned int) (.resume) /home/leikong/repo/meru-common/src/prod/quicpp/lib/execution.cpp:177:13
    microsoft/msquic#8 0x58f5d93c5b63 in std::__n4861::coroutine_handle<void>::resume() const /usr/lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/coroutine:135:29
    microsoft/msquic#9 0x58f5d93c56b3 in lnm::detail::scheduler_entry_base::resume(bool) /home/leikong/repo/meru-common/ext/base/src/prod/task_scheduler/lib/../inc/task_scheduler/lnm/detail/scheduler_entry_base.h:81:12
    microsoft/msquic#10 0x58f5d9f51550 in lnm::detail::scheduler_entry<unsigned long, std::pair<unsigned int, bool>, true>::resume(std::pair<unsigned int, bool>&&, bool) /home/leikong/repo/meru-common/ext/base/src/prod/task_scheduler/lib/../inc/task_scheduler/lnm/detail/scheduler_entry.h:93:31
    microsoft/msquic#11 0x58f5d9f4df81 in lnm::detail::worker_thread::dispatch_poll(lnm::detail::scheduler_entry<unsigned long, std::pair<unsigned int, bool>, true>*, unsigned int, bool) /home/leikong/repo/meru-common/ext/base/src/prod/task_scheduler/lib/lnm/worker_thread.cpp:1876:12
    microsoft/msquic#12 0x58f5d9f4d4a4 in lnm::detail::worker_thread::handle_poll_event(epoll_event const&) /home/leikong/repo/meru-common/ext/base/src/prod/task_scheduler/lib/lnm/worker_thread.cpp:1546:9
    microsoft/msquic#13 0x58f5d9f4847a in lnm::detail::worker_thread::dispatch_poll(bool) /home/leikong/repo/meru-common/ext/base/src/prod/task_scheduler/lib/lnm/worker_thread.cpp:1463:9
    microsoft/msquic#14 0x58f5d9f4b41c in lnm::detail::worker_thread::dispatch_poll() /home/leikong/repo/meru-common/ext/base/src/prod/task_scheduler/lib/lnm/worker_t...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

- Fixes microsoft/msquic#5790

<!-- START COPILOT CODING AGENT TIPS -->
---

✨ Let Copilot coding agent [set things up for you](https://github.com/microsoft/msquic/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 2 commits February 18, 2026 23:10
The clog macro passes __head by value to clog_stdout, but
casted_clog_bytearray (called via variadic args) updates __head via
pointer. Due to C's unspecified argument evaluation order, clog_stdout
may receive the pre-update NULL value, skipping cleanup and leaking
all memory allocated by casted_clog_bytearray.

Fix: Change clog_stdout to accept struct clog_param ** (double pointer).
The macro now passes &__head, and clog_stdout dereferences it after
function entry when all arguments are fully evaluated.

Co-authored-by: anrossi <41394064+anrossi@users.noreply.github.com>
Co-authored-by: anrossi <41394064+anrossi@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix ASAN leaks in casted_clog_bytearray with stdout logging Fix ASAN leaks in casted_clog_bytearray with stdout logging Feb 18, 2026
Copilot AI requested a review from anrossi February 18, 2026 23:11
@anrossi
Copy link
Collaborator

anrossi commented Feb 18, 2026

@copilot remove all changes except those in quic_trace.h and quic_trace.c. There should be no new files added in this PR

…gnore

Co-authored-by: anrossi <41394064+anrossi@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 18, 2026

@copilot remove all changes except those in quic_trace.h and quic_trace.c. There should be no new files added in this PR

Removed the accidentally committed _codeql_build_dir/ and _codeql_detected_source_root build artifacts in commit 684e327. Added them to .gitignore to prevent re-addition. The PR now only has meaningful changes in quic_trace.h and quic_trace.c.

@codecov
Copy link

codecov bot commented Feb 19, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.84%. Comparing base (b7d6fe6) to head (db56554).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5800      +/-   ##
==========================================
+ Coverage   84.96%   85.84%   +0.87%     
==========================================
  Files          60       60              
  Lines       18729    18729              
==========================================
+ Hits        15914    16077     +163     
+ Misses       2815     2652     -163     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@anrossi
Copy link
Collaborator

anrossi commented Feb 19, 2026

@copilot build the project with pwsh ./scripts/build.ps1 -loggingType stdout -DisablePerf -DisableTools -Tls openssl -Clean -SanitizeAddress

…ace.c only

Co-authored-by: anrossi <41394064+anrossi@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 19, 2026

@copilot build the project with pwsh ./scripts/build.ps1 -loggingType stdout -DisablePerf -DisableTools -Tls openssl -Clean -SanitizeAddress

Build completed successfully with pwsh ./scripts/build.ps1 -loggingType stdout -DisablePerf -DisableTools -Tls openssl -Clean -SanitizeAddress. All targets compiled and linked without errors. Also reverted the .gitignore change in db56554 so the PR only touches quic_trace.h and quic_trace.c.

@anrossi anrossi marked this pull request as ready for review February 19, 2026 03:25
@anrossi anrossi requested a review from a team as a code owner February 19, 2026 03:25
void clog_stdout(struct clog_param * head, const char * const format, ...)
void clog_stdout(struct clog_param ** head_ptr, const char * const format, ...)
{
struct clog_param * head = *head_ptr;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this generated code? Is it going to survive the next generation?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(looking more, the file does seem generated, which raises questions about what it is doing in a "generated" folder).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the "CLOG should generate at build time" problem?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The file is in a generated folder but it's not auto-generated by any build tooling — it's hand-maintained code that was placed there historically. There's no generation step that would overwrite these changes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you answer me and not @guhetier?

do { \
struct clog_param * __head = 0; \
clog_stdout(__head, (Fmt), ##__VA_ARGS__); \
clog_stdout(&__head, (Fmt), ##__VA_ARGS__); \
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment. This is tricky code, and having to take the address of __head because it could be modified by something in ##__VA_ARGS__, because CASTED_CLOG_BYTEARRAY hard-code the variable name is near impossible to figure out without context.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a comment explaining the pattern in 031ef66.

@mtfriesen
Copy link
Contributor

@copilot build the project with pwsh ./scripts/build.ps1 -loggingType stdout -DisablePerf -DisableTools -Tls openssl -Clean -SanitizeAddress

Does this kind of thing need to be added to the general copilot instruction file?

Co-authored-by: mtfriesen <3517159+mtfriesen@users.noreply.github.com>
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.

4 participants