Skip to content

fix(userspace/libsinsp): prevent infinite loop in ancillary data pars…#2764

Merged
poiana merged 1 commit intofalcosecurity:masterfrom
fremmi:fix-infinite-loop
Dec 22, 2025
Merged

fix(userspace/libsinsp): prevent infinite loop in ancillary data pars…#2764
poiana merged 1 commit intofalcosecurity:masterfrom
fremmi:fix-infinite-loop

Conversation

@fremmi
Copy link
Copy Markdown
Contributor

@fremmi fremmi commented Dec 19, 2025

What type of PR is this?

Uncomment one (or more) /kind <> lines:

/kind bug

/kind cleanup

/kind design

/kind documentation

/kind failing-test

/kind test

/kind feature

/kind sync

Any specific area of the project related to this PR?

Uncomment one (or more) /area <> lines:

/area API-version

/area build

/area CI

/area driver-kmod

/area driver-bpf

/area driver-modern-bpf

/area libscap-engine-bpf

/area libscap-engine-gvisor

/area libscap-engine-kmod

/area libscap-engine-modern-bpf

/area libscap-engine-nodriver

/area libscap-engine-noop

/area libscap-engine-source-plugin

/area libscap-engine-savefile

/area libscap

/area libpman

/area libsinsp

/area tests

/area proposals

Does this PR require a change in the driver versions?

/version driver-API-version-major

/version driver-API-version-minor

/version driver-API-version-patch

/version driver-SCHEMA-version-major

/version driver-SCHEMA-version-minor

/version driver-SCHEMA-version-patch

What this PR does / why we need it:

Add validation in ppm_cmsg_nxthdr to ensure cmsg_aligned_len is at least sizeof(ppm_cmsghdr) after alignment calculation.
This prevents an infinite loop when malformed ancillary data contains cmsg_len = 0xFFFFFFFFFFFFFFFF, which causes integer overflow in PPM_CMSG_ALIGN macro, resulting in cmsg_aligned_len = 0 and preventing forward progress in the loop inside rocess_recvmsg_ancillary_data

More details:
I experienced a malfunctioning caused by an infinite tight loop when we process ancillary data from recvmsg() / recvmmsg().

Look at ppm_cmsg_nxthdr

If a control message contains a broken cmsg_len value equal to 0xFFFFFFFFFFFFFFFF, we hit an integer overflow during the alignment calculation. Because of this, the aligned length becomes 0

#define PPM_CMSG_ALIGN(len) (((len) + sizeof(size_t) - 1) & (size_t) ~(sizeof(size_t) - 1))
// On 64-bit systems with sizeof(size_t) = 8:
cmsg_aligned_len = ((0xFFFFFFFFFFFFFFFF + 7) & ~7)
                 = (0x0000000000000006 & 0xFFFFFFFFFFFFFFF8)  // Overflow!
                 = 0x0000000000000000

The addition 0xFFFFFFFFFFFFFFFF + 7 wraps around to 0x0000000000000006 due to integer overflow. After masking, the result is 0.

The infinite loop happens here:

sinsp_parser::process_recvmsg_ancillary_data

	for(ppm_cmsghdr *cmsg = PPM_CMSG_FIRSTHDR(msg_ctrl, msg_ctrllen); cmsg != nullptr;
	    cmsg = PPM_CMSG_NXTHDR(msg_ctrl, msg_ctrllen, cmsg)) <--- ALWAYS THE SAME{
		// Check for malformed control message buffer:
		if(reinterpret_cast<const char *>(cmsg) < msg_ctrl ||
		   reinterpret_cast<const char *>(cmsg) >= msg_ctrl + msg_ctrllen) {
			libsinsp_logger()->format(sinsp_logger::SEV_DEBUG,
			                          "Malformed ancillary data, skipping.");
			break;
		}
		int cmsg_type;
		PPM_CMSG_UNALIGNED_READ(cmsg, cmsg_type, cmsg_type);
		if(cmsg_type != SCM_RIGHTS) {
			continue; <-- CONTINUE FOR EVER
		}

When cmsg_aligned_len = 0, the pointer advancement soon before the for statement became

cmsg = reinterpret_cast<ppm_cmsghdr *>(reinterpret_cast<char *>(cmsg) + 0);

Which issue(s) this PR fixes:

Fixes #

Special notes for your reviewer:

Does this PR introduce a user-facing change?:

NONE

…ing due to integer overflow

Add validation in ppm_cmsg_nxthdr to ensure cmsg_aligned_len is at least
sizeof(ppm_cmsghdr) after alignment calculation. This prevents an infinite
loop when malformed ancillary data contains cmsg_len = 0xFFFFFFFFFFFFFFFF,
which causes integer overflow in PPM_CMSG_ALIGN macro, resulting in
cmsg_aligned_len = 0 and preventing forward progress in the loop.

Signed-off-by: Francesco Emmi <francesco.emmi@sysdig.com>
@deepskyblue86
Copy link
Copy Markdown
Member

deepskyblue86 commented Dec 19, 2025

CC @ekoops (who did #2262)

Also relates to #2336

@github-actions
Copy link
Copy Markdown

Perf diff from master - unit tests

    12.55%    +11.74%  [.] std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_add_ref_lock_nothrow()
    11.38%     -7.90%  [.] std::__shared_count<(__gnu_cxx::_Lock_policy)2>::_M_get_use_count() const
    11.99%     -2.30%  [.] sinsp_threadinfo::get_main_thread()
    14.84%     -1.70%  [.] std::__shared_ptr<sinsp_threadinfo, (__gnu_cxx::_Lock_policy)2>::__shared_ptr(std::__weak_ptr<sinsp_threadinfo, (__gnu_cxx::_Lock_policy)2> const&, std::nothrow_t)
     4.71%     +1.25%  [.] thread_group_info::get_first_thread() const
     9.11%     -1.07%  [.] sinsp_threadinfo::update_main_fdtable()
     9.00%     -0.36%  [.] sinsp_threadinfo::get_fd_table()
     8.82%     +0.35%  [.] std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release()
     0.11%     +0.19%  [.] void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<char*>(char*, char*, std::forward_iterator_tag)
     0.10%     +0.14%  [.] std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_dispose()@plt

Heap diff from master - unit tests

peak heap memory consumption: 0B
peak RSS (including heaptrack overhead): 0B
total memory leaked: 0B

Heap diff from master - scap file

peak heap memory consumption: 0B
peak RSS (including heaptrack overhead): 0B
total memory leaked: 0B

Benchmarks diff from master

Comparing gbench_data.json to /root/actions-runner/_work/libs/libs/build/gbench_data.json
Benchmark                                                         Time             CPU      Time Old      Time New       CPU Old       CPU New
----------------------------------------------------------------------------------------------------------------------------------------------
BM_sinsp_split_mean                                            -0.0591         -0.0592           241           227           241           227
BM_sinsp_split_median                                          -0.0523         -0.0522           240           228           240           228
BM_sinsp_split_stddev                                          +0.2586         +0.2516             2             3             2             3
BM_sinsp_split_cv                                              +0.3377         +0.3304             0             0             0             0
BM_sinsp_concatenate_paths_relative_path_mean                  -0.0777         -0.0778            74            69            74            69
BM_sinsp_concatenate_paths_relative_path_median                -0.0725         -0.0725            74            68            74            68
BM_sinsp_concatenate_paths_relative_path_stddev                -0.6713         -0.6750             1             0             1             0
BM_sinsp_concatenate_paths_relative_path_cv                    -0.6436         -0.6476             0             0             0             0
BM_sinsp_concatenate_paths_empty_path_mean                     +0.0516         +0.0516            42            44            42            44
BM_sinsp_concatenate_paths_empty_path_median                   +0.0531         +0.0532            42            44            42            44
BM_sinsp_concatenate_paths_empty_path_stddev                   -0.5790         -0.5845             0             0             0             0
BM_sinsp_concatenate_paths_empty_path_cv                       -0.5996         -0.6049             0             0             0             0
BM_sinsp_concatenate_paths_absolute_path_mean                  -0.0611         -0.0611            74            69            74            69
BM_sinsp_concatenate_paths_absolute_path_median                -0.0608         -0.0608            73            69            73            69
BM_sinsp_concatenate_paths_absolute_path_stddev                -0.0727         -0.0700             0             0             0             0
BM_sinsp_concatenate_paths_absolute_path_cv                    -0.0123         -0.0094             0             0             0             0

@codecov
Copy link
Copy Markdown

codecov bot commented Dec 19, 2025

Codecov Report

❌ Patch coverage is 0% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 74.57%. Comparing base (a8fb3d2) to head (e67ecdd).
⚠️ Report is 52 commits behind head on master.

Files with missing lines Patch % Lines
userspace/libsinsp/parsers.cpp 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2764      +/-   ##
==========================================
- Coverage   76.90%   74.57%   -2.34%     
==========================================
  Files         296      292       -4     
  Lines       30875    30026     -849     
  Branches     4693     4658      -35     
==========================================
- Hits        23745    22392    -1353     
- Misses       7130     7634     +504     
Flag Coverage Δ
libsinsp 74.57% <0.00%> (-2.34%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ 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.

Copy link
Copy Markdown
Member

@deepskyblue86 deepskyblue86 left a comment

Choose a reason for hiding this comment

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

It's a defensive check and perfectly makes sense.
I wonder when this could happen (and I doubt anyone is still trying to leverage CVE-2004-1334)...

/approve

@poiana
Copy link
Copy Markdown
Contributor

poiana commented Dec 19, 2025

LGTM label has been added.

DetailsGit tree hash: f5039f0a91f83a92295d55ef1254688638cfae88

}

size_t const cmsg_aligned_len = PPM_CMSG_ALIGN(cmsg_len);
// Guard against infinite loop: ensure we advance by at least sizeof(ppm_cmsghdr)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sorry, my logic fails here. Line 3205 checks if cmsg_len is greater than sizeof(ppm_cmsghdr) and if PPM_CMSG_ALIGN just rounds up cmsg_lento the next multiple of size_t, can cmsg_aligned_len ever be less than sizeof(ppm_cmsghdr)?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I wonder when this could happen (and I doubt anyone is still trying to leverage CVE-2004-1334)...

I now saw the reference to the old overflow CVE, but is overflow still a valid case?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@terror96 , I added a More details section to the description. I think it should answer your questions.
What I described really happens, this is a real case I observed.
I didn’t have the chance yet to verify if this is related to any malicious activity or not.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thank you for the further clarification. Your observation confirms that overflow can still occur and it is indeed better to protect against it. Good job.

@ekoops
Copy link
Copy Markdown
Contributor

ekoops commented Dec 22, 2025

Hey @fremmi , thank you for this important fix! 😄

I'm reviewing the PR and evaluating inclusion in 0.23.0. Since this is an important fix, I'm pretty sure this will be included in this next release, but please @falcosecurity/libs-maintainers, remember we are in code freeze.

/hold

@ekoops
Copy link
Copy Markdown
Contributor

ekoops commented Dec 22, 2025

/hold


size_t const cmsg_aligned_len = PPM_CMSG_ALIGN(cmsg_len);
// Guard against infinite loop: ensure we advance by at least sizeof(ppm_cmsghdr)
if(cmsg_aligned_len < sizeof(ppm_cmsghdr)) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Isn't checking for 0 enough?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is consistent with the check above, I think it's better to keep it this way.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yes. But being this a defensive approach, and get stuck on a tight loop would happen not only if csm_align_len is 0 but if it is < than sizeof(ppm_cmsghdr) (I don't even care here if this is a possible case) I preferred doing this way

@ekoops
Copy link
Copy Markdown
Contributor

ekoops commented Dec 22, 2025

Your check indeed covers an uncovered case. While still wondering how is possible that, in the context of a successful recvmsg() call, we can get a single control message having a length bigger than the specified length of all control messages, I agree that the patch fixes the issue in this case.
Since I originally got the code from glibc, I explored again glibc sources to find any patch addressing similar issues. I found this: bminor/glibc@9c443ac
I guess the check in bminor/glibc@9c443ac#diff-dee18faf2c4354998559dd62c4595465d55112d9665e6f25a5adec32c8cd965bR285-R288 actually fixes this issue, but I need to further investigate.

@ekoops ekoops added this to the 0.23.0 milestone Dec 22, 2025
@ekoops
Copy link
Copy Markdown
Contributor

ekoops commented Dec 22, 2025

I'm including this fix in the upcoming 0.23.0 release as I believe this for sure fixes the discussed issue, and we want to release libs by tomorrow.
However, I'm also gonna open an issue to track the alignment of ppm_cmsg_nxthdr with the new glibc definition: https://github.com/bminor/glibc/blob/0b8a996f44b5f4c02991f02cd12bf05b17db4576/sysdeps/unix/sysv/linux/cmsg_nxthdr.c#L23-L60

Copy link
Copy Markdown
Contributor

@ekoops ekoops left a comment

Choose a reason for hiding this comment

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

/approve
/remove-hold

@github-project-automation github-project-automation bot moved this from Todo to In progress in Falco Roadmap Dec 22, 2025
@poiana
Copy link
Copy Markdown
Contributor

poiana commented Dec 22, 2025

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: deepskyblue86, ekoops, fremmi

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:
  • OWNERS [deepskyblue86,ekoops]

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@poiana poiana merged commit 9e6a8cc into falcosecurity:master Dec 22, 2025
45 of 47 checks passed
@github-project-automation github-project-automation bot moved this from In progress to Done in Falco Roadmap Dec 22, 2025
@terror96
Copy link
Copy Markdown
Contributor

I'm including this fix in the upcoming 0.23.0 release as I believe this for sure fixes the discussed issue, and we want to release libs by tomorrow. However, I'm also gonna open an issue to track the alignment of ppm_cmsg_nxthdr with the new glibc definition: https://github.com/bminor/glibc/blob/0b8a996f44b5f4c02991f02cd12bf05b17db4576/sysdeps/unix/sysv/linux/cmsg_nxthdr.c#L23-L60

If I'm following correctly the way glibc fixes the issue that solution also confirms that the buffer holds enough data. Let us imagine cmsg_len is faulty (too big) but not too big to flip over, then if(cmsg_aligned_len < sizeof(ppm_cmsghdr)) { passes but ((size_t) (__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr - __size_needed) < __cmsg->cmsg_len) will not. However, I'm not sure if my head just exploded...

@ekoops
Copy link
Copy Markdown
Contributor

ekoops commented Dec 23, 2025

Totally agree with you @terror96 . I'll open the syncing PR after libs 0.23.0 is out.

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

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

6 participants