@cryptotaxi247 / netdata-1 / commits / 9d076a1f8

Fix Coverity issues (Timezone) (#21971)

thiagoftsm committed Mar 19, 2026 at 17:46 UTC 9d076a1f85816cddb5dd137112c4f6b18e006b94
3 files changed +55 -8
src/daemon/analytics.c
+41 -5
@@ -857,6 +857,8 @@ static bool timezone_user_configured = false;
857 // fallback which only produces bare abbreviations like "CEST" or "PST".
858 static bool timezone_is_tzdb_name = false;
859
860 +static bool timezone_abbrev_normalize(char *dst, size_t dst_size, const char *src);
861 +
862 bool system_timezone_is_user_configured(void) {
863 return timezone_user_configured;
864 }
@@ -922,14 +924,28 @@ void get_system_timezone(void)
924 // inicfg_get auto-populates it with the detected default.
925 timezone_user_configured = inicfg_exists(&netdata_config, CONFIG_SECTION_GLOBAL, "timezone");
926
927 + char safe_timezone[FILENAME_MAX + 1];
928 + const char *default_timezone = timezone;
929 +
930 + if (timezone_is_tzdb_name) {
931 + if (!timezone_name_is_safe_tzdb_path(timezone)) {
932 + netdata_log_error("TIMEZONE: detected unsafe tzdb timezone '%s', ignoring", timezone);
933 + default_timezone = "unknown";
934 + }
935 + } else if (!timezone_abbrev_normalize(safe_timezone, sizeof(safe_timezone), timezone)) {
936 + default_timezone = "unknown";
937 + } else {
938 + default_timezone = safe_timezone;
939 + }
940 +
941 // inicfg_get returns a config-system-owned pointer, stable for the process lifetime
926 - const char *configured_tz = inicfg_get(&netdata_config, CONFIG_SECTION_GLOBAL, "timezone", timezone);
942 + const char *configured_tz = inicfg_get(&netdata_config, CONFIG_SECTION_GLOBAL, "timezone", default_timezone);
943
944 // Treat "timezone =" (empty value) as not user-configured,
945 // and fall back to the auto-detected timezone.
946 if (timezone_user_configured && (!configured_tz || !*configured_tz)) {
947 timezone_user_configured = false;
932 - configured_tz = timezone;
948 + configured_tz = default_timezone;
949 }
950
951 // If the user explicitly configured a timezone, treat it as a valid tzdb name
@@ -959,7 +975,7 @@ static inline int64_t tzif_read_be64(const unsigned char *src) {
975 (int64_t)(uint64_t)src[7];
976 }
977
962 -static bool timezone_name_is_safe_tzdb_path(const char *timezone) {
978 +bool timezone_name_is_safe_tzdb_path(const char *timezone) {
979 if (!timezone || !*timezone || *timezone == '/')
980 return false;
981
@@ -971,6 +987,25 @@ static bool timezone_name_is_safe_tzdb_path(const char *timezone) {
987 return true;
988 }
989
990 +static bool timezone_abbrev_normalize(char *dst, size_t dst_size, const char *src) {
991 + if (!dst || dst_size < 2 || !src || !*src)
992 + return false;
993 +
994 + size_t len = 0;
995 + char *end = dst + dst_size - 1;
996 +
997 + while (*src && dst < end) {
998 + if (!(isalnum((uint8_t)*src) || *src == '_' || *src == '+' || *src == '-'))
999 + return false;
1000 +
1001 + *dst++ = *src++;
1002 + len++;
1003 + }
1004 +
1005 + *dst = '\0';
1006 + return len != 0 && *src == '\0';
1007 +}
1008 +
1009 static bool timezone_info_from_tm(struct tm *tmp, char *abbrev, size_t abbrev_size, int32_t *offset) {
1010 if (!tmp)
1011 return false;
@@ -1219,6 +1254,7 @@ cleanup:
1254 void refresh_system_timezone(const char *timezone, bool is_tzdb_name) {
1255 time_t t;
1256 char abbrev[64];
1257 + char safe_abbrev[64];
1258 const char *new_abbrev = "UTC";
1259 int32_t new_offset = 0;
1260
@@ -1249,8 +1285,8 @@ void refresh_system_timezone(const char *timezone, bool is_tzdb_name) {
1285 if (!ok)
1286 ok = current_process_timezone_info(t, abbrev, sizeof(abbrev), &new_offset);
1287
1252 - if (ok && *abbrev)
1253 - new_abbrev = abbrev;
1288 + if (ok && timezone_abbrev_normalize(safe_abbrev, sizeof(safe_abbrev), abbrev))
1289 + new_abbrev = safe_abbrev;
1290
1291 // Atomically update the system timezone triplet
1292 system_tz_set(timezone, new_abbrev, new_offset);
src/daemon/analytics.h
+1
@@ -91,6 +91,7 @@ void get_system_timezone(void);
91 bool system_timezone_is_user_configured(void);
92 bool system_timezone_is_tzdb_name(void);
93 const char *detect_system_timezone_name(char *buffer, size_t buffer_size);
94 +bool timezone_name_is_safe_tzdb_path(const char *timezone);
95 void refresh_system_timezone(const char *timezone, bool is_tzdb_name);
96 void analytics_reset(void);
97 void analytics_init(void);
src/daemon/pulse/pulse-daemon.c
+13 -3
@@ -102,12 +102,22 @@ static void pulse_daemon_timezone_do(bool extended __maybe_unused) {
102 // Auto-detected timezone — re-detect to pick up system changes.
103 char buf[FILENAME_MAX + 1];
104 const char *detected = detect_system_timezone_name(buf, sizeof(buf));
105 - if (detected) {
105 + if (detected && timezone_name_is_safe_tzdb_path(detected)) {
106 refresh_system_timezone(detected, true);
107 } else {
108 - // Detection failed — use the stored name with its original tzdb flag.
108 + if (detected)
109 + // Detected a timezone that failed the safe-path check; skip it.
110 + netdata_log_error("TIMEZONE: detected unsafe timezone name '%s', ignoring", detected);
111 +
112 + // Detection failed or was rejected — re-use the stored name with its original tzdb flag.
113 + // Validate the stored name before reusing it: a previously persisted unsafe tzdb path
114 + // must not reach refresh_system_timezone() and propagate to labels or ACLK.
115 SYSTEM_TZ tz = system_tz_get();
110 - refresh_system_timezone(tz.timezone, system_timezone_is_tzdb_name());
116 + bool is_tzdb = system_timezone_is_tzdb_name();
117 + if (!is_tzdb || timezone_name_is_safe_tzdb_path(tz.timezone))
118 + refresh_system_timezone(tz.timezone, is_tzdb);
119 + else
120 + netdata_log_error("TIMEZONE: stored unsafe tzdb timezone name '%s', ignoring", tz.timezone ? tz.timezone : "(null)");
121 system_tz_free(&tz);
122 }
123 }