-
Type:
Problem report
-
Resolution: Unresolved
-
Priority:
Trivial
-
None
-
Affects Version/s: 7.4.14
-
Component/s: None
-
None
-
Environment:Zabbix Agent (Active checks)
Build with AddressSanitizer (ASan) and libFuzzer
Linux x86_64
[Steps to reproduce]
1. Send a JSON response from the server containing active checks where the "exp_delimiter" field is an empty string (e.g., "").
2. The agent parses the list of checks and assigns the NUL terminator '\0' to the delimiter variable.
[Result]
The Zabbix agent crashes with a heap-buffer-overflow.
AddressSanitizer reports an out-of-bounds read in regexp_match_ex_substring_list.
[Root cause]
When parsing in `parse_list_of_checks`, the `exp_delimiter` value is assigned as follows:
exp_delimiter = tmp[0];
If the JSON field is an empty string, `exp_delimiter` becomes `'\0'`.
Later, `regexp_match_ex_substring_list()` is called with `delimiter = '\0'`:
for (s = pattern; '\0' != *s && ZBX_REGEXP_MATCH != ret;)
{
if (NULL != (c = strchr(s, delimiter)))
*c = '\0';
// ...
if (NULL != c)
{
*c = delimiter;
s = ++c; // <-- ISSUE HERE
}
else
break;
}
On the first iteration, `strchr(s, delimiter)` returns a pointer to the terminating NUL byte of the `pattern` string. Then `s = ++c` advances the pointer past this NUL byte. On the second iteration, the condition `'\0' != *s` performs an out-of-bounds read past the end of the allocated `pattern` buffer.
[ASan Log]
==37==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7b38a33e2d80 at pc 0x000000926282 bp 0x7fff945eb9a0 sp 0x7fff945eb998 READ of size 1 at 0x7b38a33e2d80 thread T0 #0 0x000000926281 in regexp_match_ex_substring_list src/libs/zbxregexp/zbxregexp.c:1368:28 #1 0x0000009286be in zbx_regexp_sub_ex2 src/libs/zbxregexp/zbxregexp.c:1651:11 #2 0x0000008b96cb in zbx_read2 src/zabbix_agent/logfiles/logfiles.c:2254:19 #3 0x00000087d2c4 in process_log src/zabbix_agent/logfiles/logfiles.c:2461:25 #4 0x00000086f6ae in process_logrt src/zabbix_agent/logfiles/logfiles.c:3511:11 #5 0x000000863686 in process_log_check src/zabbix_agent/logfiles/logfiles.c:4100:8 #6 0x00000082f7a0 in process_active_checks src/zabbix_agent/active_checks/active_checks.c:1730:10 #7 0x000000835741 in fuzz_active_checks_run src/zabbix_agent/active_checks/active_checks.c:2208:3 #8 0x000000596e4c in TestOneProtoInput(zbxfuzz::JSON_opt const&) assets/fuzz_zbx_active_checks.cc:47:3 #9 0x000000596a49 in LLVMFuzzerTestOneInput assets/fuzz_zbx_active_checks.cc:41:1
0x7b38a33e2d80 is located 0 bytes after 16-byte region [0x7b38a33e2d70,0x7b38a33e2d80)
allocated by thread T0 here:
#0 0x000000531f7e in strdup #1 0x0000009d2f61 in zbx_strdup2 src/libs/zbxcommon/misc.c:193:8 #2 0x00000092336c in zbx_add_regexp_ex src/libs/zbxregexp/zbxregexp.c:1184:23 #3 0x000000838dbd in parse_list_of_checks src/zabbix_agent/active_checks/active_checks.c:700:4
[Proposed fix]
diff --git a/src/zabbix_agent/active_checks/active_checks.c b/src/zabbix_agent/active_checks/active_checks.cindex 2398e26..f1beefe 100644--- a/src/zabbix_agent/active_checks/active_checks.c+++ b/src/zabbix_agent/active_checks/active_checks.c@@ -680,7 +680,8 @@ static int parse_list_of_checks(char *str, const char *host, unsigned short port expression_type = atoi(tmp);- if (SUCCEED != zbx_json_value_by_name(&jp_row, "exp_delimiter", tmp, sizeof(tmp), NULL))+ if (SUCCEED != zbx_json_value_by_name(&jp_row, "exp_delimiter", tmp, sizeof(tmp), NULL) ||+ '\0' == *tmp) { zabbix_log(LOG_LEVEL_WARNING, "cannot retrieve value of tag \"%s\"", "exp_delimiter"); continue;