From 9d625b10a1c54ea2258fe9a1f4b49f671efee2b3 Mon Sep 17 00:00:00 2001 From: Nick Anderson Date: Wed, 1 Jul 2026 14:11:09 -0500 Subject: [PATCH 1/6] test: add acceptance test for CFE-2663 (empty trailing region) An insert_lines promise using select_region with select_end_match_eof cannot select the region of the final section when that section header is the last line of the file. The region is empty and sits at the end of the file, SelectRegion() rejects it, and the insertion fails with "could not select an edit region". This test fails today and will pass once SelectRegion() honors select_end_match_eof for an empty region at end of file. Ticket: CFE-2663 Changelog: None Signed-off-by: Nick Anderson (cherry picked from commit ebf30a2e8a6cb7507c15b71e1fd73c246705225c) --- tests/acceptance/31_tickets/CFE-2663/test.cf | 81 ++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 tests/acceptance/31_tickets/CFE-2663/test.cf diff --git a/tests/acceptance/31_tickets/CFE-2663/test.cf b/tests/acceptance/31_tickets/CFE-2663/test.cf new file mode 100644 index 0000000000..ffcf713441 --- /dev/null +++ b/tests/acceptance/31_tickets/CFE-2663/test.cf @@ -0,0 +1,81 @@ +body file control +{ + inputs => { "../../default.sub.cf" }; +} + +bundle agent __main__ +{ + methods: + "bundlesequence" usebundle => default("$(this.promise_filename)"); +} + +bundle agent init +{ + vars: + # No trailing newline in the strings, so the "[section2]" header is + # genuinely the last line of the "actual" file. The region selected for + # section2 is therefore empty and sits at the end of the file. + "actual" string => "[section] +keyone=valueone +[section2]"; + + "expected" string => "[section] +keyone=valueone +[section2] +keytwo=valuetwo"; + + "files" slist => { "actual", "expected" }; + + files: + "$(G.testfile).$(files)" + create => "true", + edit_line => init_insert("$(init.$(files))"), + edit_defaults => init_empty; +} + +bundle edit_line init_insert(str) +{ + insert_lines: + "$(str)"; +} + +body edit_defaults init_empty +{ + empty_file_before_editing => "true"; +} + +bundle agent test +{ + meta: + "description" + string => "Insertion into an empty trailing region (the select_start delimiter is the last line of the file) must succeed when select_end_match_eof is true", + meta => { "CFE-2663" }; + + files: + "$(G.testfile).actual" + edit_line => insert_into_trailing_section; +} + +bundle edit_line insert_into_trailing_section +{ + insert_lines: + "keytwo=valuetwo" + select_region => ini_section("section2"); +} + +body select_region ini_section(x) +# @brief Restrict the edit to the lines in section [x], matching to EOF when +# [x] is the final section in the file. +{ + select_start => "\[$(x)\]\s*"; + select_end => "\[.*\]\s*"; + select_end_match_eof => "true"; +} + +bundle agent check +{ + methods: + "any" usebundle => dcs_check_diff("$(G.testfile).actual", + "$(G.testfile).expected", + "$(this.promise_filename)"); +} From b4ad8ef9d87fc824761f2af8f46e4365df11c4e6 Mon Sep 17 00:00:00 2001 From: Nick Anderson Date: Fri, 10 Jul 2026 15:42:31 -0500 Subject: [PATCH 2/6] Reformatted to satisfy lint --- tests/acceptance/31_tickets/CFE-2663/test.cf | 53 ++++++++++---------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/tests/acceptance/31_tickets/CFE-2663/test.cf b/tests/acceptance/31_tickets/CFE-2663/test.cf index ffcf713441..27300ef480 100644 --- a/tests/acceptance/31_tickets/CFE-2663/test.cf +++ b/tests/acceptance/31_tickets/CFE-2663/test.cf @@ -12,70 +12,69 @@ bundle agent __main__ bundle agent init { vars: - # No trailing newline in the strings, so the "[section2]" header is - # genuinely the last line of the "actual" file. The region selected for - # section2 is therefore empty and sits at the end of the file. - "actual" string => "[section] + # No trailing newline in the strings, so the "[section2]" header is + # genuinely the last line of the "actual" file. The region selected for + # section2 is therefore empty and sits at the end of the file. + "actual" string => "[section] keyone=valueone [section2]"; - - "expected" string => "[section] + "expected" string => "[section] keyone=valueone [section2] keytwo=valuetwo"; - - "files" slist => { "actual", "expected" }; + "files" slist => { "actual", "expected" }; files: - "$(G.testfile).$(files)" - create => "true", - edit_line => init_insert("$(init.$(files))"), - edit_defaults => init_empty; + "$(G.testfile).$(files)" + create => "true", + edit_line => init_insert("$(init.$(files))"), + edit_defaults => init_empty; } bundle edit_line init_insert(str) { insert_lines: - "$(str)"; + "$(str)"; } body edit_defaults init_empty { - empty_file_before_editing => "true"; + empty_file_before_editing => "true"; } bundle agent test { meta: - "description" - string => "Insertion into an empty trailing region (the select_start delimiter is the last line of the file) must succeed when select_end_match_eof is true", - meta => { "CFE-2663" }; + "description" + string => "Insertion into an empty trailing region (the select_start delimiter is the last line of the file) must succeed when select_end_match_eof is true", + meta => { "CFE-2663" }; files: - "$(G.testfile).actual" - edit_line => insert_into_trailing_section; + "$(G.testfile).actual" edit_line => insert_into_trailing_section; } bundle edit_line insert_into_trailing_section { insert_lines: - "keytwo=valuetwo" - select_region => ini_section("section2"); + "keytwo=valuetwo" select_region => ini_section("section2"); } body select_region ini_section(x) # @brief Restrict the edit to the lines in section [x], matching to EOF when # [x] is the final section in the file. { - select_start => "\[$(x)\]\s*"; - select_end => "\[.*\]\s*"; - select_end_match_eof => "true"; + select_start => "\[$(x)\]\s*"; + select_end => "\[.*\]\s*"; + select_end_match_eof => "true"; } bundle agent check { methods: - "any" usebundle => dcs_check_diff("$(G.testfile).actual", - "$(G.testfile).expected", - "$(this.promise_filename)"); + "any" + usebundle => dcs_check_diff( + "$(G.testfile).actual", + "$(G.testfile).expected", + "$(this.promise_filename)" + ); } From 966e5787fcf07d2e837e171c10112979c7609ae7 Mon Sep 17 00:00:00 2001 From: Nick Anderson Date: Wed, 1 Jul 2026 17:05:56 -0500 Subject: [PATCH 3/6] Fixed select_region failing to select an empty region at end of file When select_region matched a start delimiter on the last line of the file and include_start_delimiter was false, SelectRegion() rejected the region as an "empty region at the end of file" and the edit failed with "could not select an edit region". This happened even when select_end_match_eof was true, which is meant to let the region extend to the end of the file. Now the empty region at end of file is accepted when select_end_match_eof is set, so a trailing section (its header being the last line of the file) can still be populated by insert_lines and similar promises. Ticket: CFE-2663 Changelog: Title Signed-off-by: Nick Anderson (cherry picked from commit c7bcc4452e2d2b98e6fd04819182c2ed798937ee) --- cf-agent/files_editline.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cf-agent/files_editline.c b/cf-agent/files_editline.c index 303f7deb38..db52b2db15 100644 --- a/cf-agent/files_editline.c +++ b/cf-agent/files_editline.c @@ -869,7 +869,13 @@ If no such region matches, begin_ptr and end_ptr should point to NULL { if (!include_start) { - if (ip->next == NULL) + /* The start delimiter is the last line of the file, so the + * region (which excludes the delimiter) is empty and sits + * at end of file. Reject it only when select_end_match_eof + * is not set. Otherwise the empty region at EOF is a valid, + * selectable region so that e.g. a trailing INI section can + * still be populated (CFE-2663). */ + if (ip->next == NULL && !a->region.select_end_match_eof) { Log(LOG_LEVEL_VERBOSE, "The promised start pattern '%s' found an empty region at the end of file '%s'", From 3d75b181ab2f84ebe3fb1f5ea9360b71de363a73 Mon Sep 17 00:00:00 2001 From: Nick Anderson Date: Fri, 10 Jul 2026 20:52:04 -0500 Subject: [PATCH 4/6] Bump libntech from `944af92` to `11be681` Pulls in the macOS build fixes from ENT-14208: the deprecated readdir_r use in unix_dir.c (PR #280) and the switch to __APPLE__ instead of DARWIN (PR #281). This unblocks the macOS build for the CFE-2663 backport. Stops deliberately at the ent-14208 merge, short of the CFE-3629 compiler-flag rework (libntech 257982d). That rework enables -Wgnu-folding-constant as an error, which breaks the pre-existing VLA in libpromises/evalfunction.c (char key[key_len]) that master has since removed but these backport branches still carry. Ticket: ENT-14208 Changelog: None Signed-off-by: Nick Anderson --- libntech | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libntech b/libntech index 944af926a7..11be6817ae 160000 --- a/libntech +++ b/libntech @@ -1 +1 @@ -Subproject commit 944af926a78c2410075419145510c4f4beb2901d +Subproject commit 11be6817ae8067aa3a1626a3871deb872093e0e2 From 0874984d97a121f583a97e8a9c4911ab8514e314 Mon Sep 17 00:00:00 2001 From: Craig Comstock Date: Wed, 24 Jun 2026 10:49:50 -0500 Subject: [PATCH 5/6] Refactored variable length array code to use alloca() While VLAs are supported in C99 and C11, they do pose some portability issues so best to avoid their use. alloca() allocates on the stack frame and is free'd on exit from the function so essentially equivalent. Ticket: ENT-14208 Changelog: none (cherry picked from commit 6e952013b0c0d475102ec48c2907025d1aa9a629) --- cf-serverd/server_tls.c | 4 ++-- libpromises/evalfunction.c | 5 ++--- libpromises/ornaments.c | 2 +- libpromises/syslog_client.c | 5 +++-- tests/acceptance/mock_package_manager.c | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cf-serverd/server_tls.c b/cf-serverd/server_tls.c index edc185237b..10f0794320 100644 --- a/cf-serverd/server_tls.c +++ b/cf-serverd/server_tls.c @@ -226,8 +226,8 @@ bool ServerTLSPeek(ConnectionInfo *conn_info) const int peek_size = CF_INBAND_OFFSET + sizeof("CAUTH"); - char buf[peek_size]; - ssize_t got = recv(ConnectionInfoSocket(conn_info), buf, sizeof(buf), MSG_PEEK); + char *buf = alloca(peek_size); + ssize_t got = recv(ConnectionInfoSocket(conn_info), buf, peek_size, MSG_PEEK); assert(got <= peek_size); if (got < 0) { diff --git a/libpromises/evalfunction.c b/libpromises/evalfunction.c index 5ab935fd77..d9030b1eda 100644 --- a/libpromises/evalfunction.c +++ b/libpromises/evalfunction.c @@ -7396,9 +7396,8 @@ static FnCallResult FnCallClassFilterCsv(EvalContext *ctx, } else { - size_t const key_len = PRINTSIZE(size_t); - char key[key_len]; - xsnprintf(key, key_len, "%zu", i); + char key[PRINTSIZE(size_t)]; + xsnprintf(key, PRINTSIZE(size_t), "%zu", i); JsonObjectAppendString(class_container, key, diff --git a/libpromises/ornaments.c b/libpromises/ornaments.c index ca37e98d88..d43c7c56ff 100644 --- a/libpromises/ornaments.c +++ b/libpromises/ornaments.c @@ -156,7 +156,7 @@ void PromiseBanner(EvalContext *ctx, const Promise *pp) } const size_t n = 2*CF_MAXFRAGMENT + 3; - char pretty_promise_name[n+1]; + char *pretty_promise_name = alloca(n+1); pretty_promise_name[0] = '\0'; StringAppendAbbreviatedPromise(pretty_promise_name, pp->promiser, n, CF_MAXFRAGMENT); Log(LOG_LEVEL_VERBOSE, "P: Promiser/affected object: '%s'", pretty_promise_name); diff --git a/libpromises/syslog_client.c b/libpromises/syslog_client.c index 0b68048c56..0b55a0cc91 100644 --- a/libpromises/syslog_client.c +++ b/libpromises/syslog_client.c @@ -27,6 +27,8 @@ #include + +#define RFC3164_LENGTH 1024 /* * Set by cf-agent/cf-serverd from body agent/server control. */ @@ -112,8 +114,7 @@ void RemoteSysLog(int log_priority, const char *log_string) } else { - const size_t rfc3164_len = 1024; - char message[rfc3164_len]; + char message[RFC3164_LENGTH]; char timebuffer[26]; pid_t pid = getpid(); diff --git a/tests/acceptance/mock_package_manager.c b/tests/acceptance/mock_package_manager.c index 70ac0d2cd8..dea326bad1 100644 --- a/tests/acceptance/mock_package_manager.c +++ b/tests/acceptance/mock_package_manager.c @@ -8,7 +8,7 @@ static char AVAILABLE_PACKAGES_FILE_NAME[PATH_MAX]; static char INSTALLED_PACKAGES_FILE_NAME[PATH_MAX]; -static const int MAX_PACKAGE_ENTRY_LENGTH = 256; +#define MAX_PACKAGE_ENTRY_LENGTH 256 #define DEFAULT_ARCHITECTURE "x666" From 7df2f46c8bb28964486d9a42c04a5bc860461aca Mon Sep 17 00:00:00 2001 From: Nick Anderson Date: Fri, 10 Jul 2026 22:20:12 -0500 Subject: [PATCH 6/6] Fixed CFE-2663 acceptance test include path on 3.24.x The shared acceptance-test include is named default.cf.sub on 3.24.x (1998 tests use that name), but the CFE-2663 test was cherry-picked from master where it is default.sub.cf. cf-promises could not stat the file, so the policy failed validation and the test reported UNEXPECTED FAILURE. Point the include at the 3.24.x name. Ticket: CFE-2663 Changelog: None Signed-off-by: Nick Anderson --- tests/acceptance/31_tickets/CFE-2663/test.cf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/acceptance/31_tickets/CFE-2663/test.cf b/tests/acceptance/31_tickets/CFE-2663/test.cf index 27300ef480..61e05984e0 100644 --- a/tests/acceptance/31_tickets/CFE-2663/test.cf +++ b/tests/acceptance/31_tickets/CFE-2663/test.cf @@ -1,6 +1,6 @@ body file control { - inputs => { "../../default.sub.cf" }; + inputs => { "../../default.cf.sub" }; } bundle agent __main__