@cryptotaxi247 / netdata-1 / commits / a20869aa3

fix crashes identified by sentry (#19856)

* check for the existance of rc->rrdset * avoid division by zero * format rfc3339 days avoiding sprintf

Costa Tsaousis committed Mar 13, 2025 at 18:38 UTC a20869aa37971eecbbfe79ec11351d79ef106ab9
3 files changed +153 -25
src/collectors/cgroups.plugin/cgroup-charts.c
+2 -2
@@ -449,7 +449,7 @@ void update_mem_pgfaults_chart(struct cgroup *cg) {
449 }
450
451 void update_mem_usage_limit_chart(struct cgroup *cg, unsigned long long memory_limit) {
452 - if (is_cgroup_systemd_service(cg))
452 + if (is_cgroup_systemd_service(cg) || !memory_limit)
453 return;
454
455 RRDSET *chart = cg->st_mem_usage_limit;
@@ -488,7 +488,7 @@ void update_mem_usage_limit_chart(struct cgroup *cg, unsigned long long memory_l
488 }
489
490 void update_mem_utilization_chart(struct cgroup *cg, unsigned long long memory_limit) {
491 - if (is_cgroup_systemd_service(cg))
491 + if (is_cgroup_systemd_service(cg) || !memory_limit)
492 return;
493
494 RRDSET *chart = cg->st_mem_utilization;
src/health/health_variable.c
+1 -1
@@ -36,7 +36,7 @@ struct variable_lookup_job {
36 };
37
38 static void variable_lookup_add_result_with_score(struct variable_lookup_job *vbd, NETDATA_DOUBLE n, RRDSET *st, const char *source __maybe_unused) {
39 - if(vbd->score.last_rrdset != st) {
39 + if(vbd->score.last_rrdset != st && vbd->rc->rrdset) {
40 vbd->score.last_rrdset = st;
41 vbd->score.last_score = rrdlabels_common_count(vbd->rc->rrdset->rrdlabels, st->rrdlabels);
42 }
src/libnetdata/datetime/rfc3339.c
+150 -22
@@ -4,8 +4,71 @@
4
5 #include "rfc3339.h"
6
7 +// Helper functions for safe printing of date/time components
8 +// These functions don't add a null terminator and return only the exact count of bytes written
9 +
10 +static inline size_t print_4digit_year(char *buffer, size_t size, int year) {
11 + if (!buffer || size < 4) return 0; // Need at least 4 digits
12 +
13 + // Ensure year is in valid range (0-9999)
14 + year = year < 0 ? 0 : (year > 9999 ? 9999 : year);
15 +
16 + buffer[0] = '0' + (year / 1000) % 10;
17 + buffer[1] = '0' + (year / 100) % 10;
18 + buffer[2] = '0' + (year / 10) % 10;
19 + buffer[3] = '0' + year % 10;
20 +
21 + return 4;
22 +}
23 +
24 +static inline size_t print_2digit(char *buffer, size_t size, int value) {
25 + if (!buffer || size < 2) return 0; // Need at least 2 digits
26 +
27 + // Ensure value is in valid range (0-99)
28 + value = value < 0 ? 0 : (value > 99 ? 99 : value);
29 +
30 + buffer[0] = '0' + (value / 10) % 10;
31 + buffer[1] = '0' + value % 10;
32 +
33 + return 2;
34 +}
35 +
36 +static inline size_t print_fraction(char *buffer, size_t size, usec_t fraction, size_t digits) {
37 + if (!buffer || size < digits) return 0;
38 +
39 + // Validate and cap the number of digits
40 + digits = digits < 1 ? 1 : (digits > 9 ? 9 : digits);
41 +
42 + // Calculate divisor to get correct precision
43 + usec_t divisor = 1;
44 + for (size_t i = 0; i < 6 - digits; i++)
45 + divisor *= 10;
46 +
47 + // Calculate the fraction to print
48 + fraction = fraction / divisor;
49 +
50 + // Ensure fraction won't exceed the requested number of digits
51 + usec_t max_value = 1;
52 + for (size_t i = 0; i < digits; i++)
53 + max_value *= 10;
54 + max_value--;
55 +
56 + fraction = fraction > max_value ? max_value : fraction;
57 +
58 + // Print the fraction with leading zeros
59 + usec_t remaining = fraction;
60 +
61 + // Setup working backwards from least significant digit
62 + for (int i = digits - 1; i >= 0; i--) {
63 + buffer[i] = '0' + (remaining % 10);
64 + remaining /= 10;
65 + }
66 +
67 + return digits;
68 +}
69 +
70 size_t rfc3339_datetime_ut(char *buffer, size_t len, usec_t now_ut, size_t fractional_digits, bool utc) {
8 - if (!buffer || len == 0)
71 + if (!buffer || len < 20) // Minimum size for YYYY-MM-DDThh:mm:ssZ
72 return 0;
73
74 time_t t = (time_t)(now_ut / USEC_PER_SEC);
@@ -21,43 +84,108 @@ size_t rfc3339_datetime_ut(char *buffer, size_t len, usec_t now_ut, size_t fract
84 return 0;
85 }
86
24 - size_t used_length = strftime(buffer, len, "%Y-%m-%dT%H:%M:%S", tmp);
25 - if (used_length == 0) {
26 - buffer[0] = '\0';
27 - return 0;
28 - }
87 + size_t pos = 0;
88 +
89 + // Year (4 digits)
90 + if (len - pos < 4) goto finish;
91 + pos += print_4digit_year(&buffer[pos], len - pos, tmp->tm_year + 1900);
92 +
93 + // Month separator
94 + if (len - pos < 1) goto finish;
95 + buffer[pos++] = '-';
96 +
97 + // Month (2 digits)
98 + if (len - pos < 2) goto finish;
99 + pos += print_2digit(&buffer[pos], len - pos, tmp->tm_mon + 1);
100 +
101 + // Day separator
102 + if (len - pos < 1) goto finish;
103 + buffer[pos++] = '-';
104 +
105 + // Day (2 digits)
106 + if (len - pos < 2) goto finish;
107 + pos += print_2digit(&buffer[pos], len - pos, tmp->tm_mday);
108 +
109 + // T separator
110 + if (len - pos < 1) goto finish;
111 + buffer[pos++] = 'T';
112 +
113 + // Hour (2 digits)
114 + if (len - pos < 2) goto finish;
115 + pos += print_2digit(&buffer[pos], len - pos, tmp->tm_hour);
116 +
117 + // Minute separator
118 + if (len - pos < 1) goto finish;
119 + buffer[pos++] = ':';
120
30 - if (fractional_digits >= 1 && fractional_digits <= 9) {
31 - int fractional_part = (int)(now_ut % USEC_PER_SEC);
32 - if (fractional_part && len - used_length > fractional_digits + 1) {
33 - char format[] = ".%01d";
34 - format[3] = (char)('0' + fractional_digits);
121 + // Minute (2 digits)
122 + if (len - pos < 2) goto finish;
123 + pos += print_2digit(&buffer[pos], len - pos, tmp->tm_min);
124
36 - // Adjust fractional part
37 - fractional_part /= (int)pow(10, 6 - fractional_digits);
125 + // Second separator
126 + if (len - pos < 1) goto finish;
127 + buffer[pos++] = ':';
128
39 - used_length += snprintf(buffer + used_length, len - used_length,
40 - format, fractional_part);
129 + // Second (2 digits)
130 + if (len - pos < 2) goto finish;
131 + pos += print_2digit(&buffer[pos], len - pos, tmp->tm_sec);
132 +
133 + // Add fractional part if requested
134 + if (fractional_digits > 9) fractional_digits = 9;
135 + if (fractional_digits) {
136 + usec_t fractional_part = now_ut % USEC_PER_SEC;
137 +
138 + if (fractional_part > 0) {
139 + // Need space for decimal point and digits
140 + if (len - pos < fractional_digits + 1) goto finish;
141 +
142 + buffer[pos++] = '.';
143 + pos += print_fraction(&buffer[pos], len - pos, fractional_part, fractional_digits);
144 }
145 }
146
147 + // Add timezone information
148 if (utc) {
45 - if (used_length + 1 < len) {
46 - buffer[used_length++] = 'Z';
47 - buffer[used_length] = '\0';
48 - }
149 + if (len - pos < 1) goto finish;
150 + buffer[pos++] = 'Z';
151 }
152 else {
153 long offset = tmbuf.tm_gmtoff;
154 int hours = (int)(offset / 3600);
155 int minutes = abs((int)((offset % 3600) / 60));
156
55 - if (used_length + 7 < len) { // Space for "+HH:MM\0"
56 - used_length += snprintf(buffer + used_length, len - used_length, "%+03d:%02d", hours, minutes);
157 + // Check if timezone is UTC
158 + if (hours == 0 && minutes == 0) {
159 + if (len - pos < 1) goto finish;
160 + buffer[pos++] = 'Z';
161 + }
162 + else {
163 + // Need space for sign, hours, colon, minutes (6 chars total)
164 + if (len - pos < 6) goto finish;
165 +
166 + // Add timezone offset
167 + buffer[pos++] = (hours >= 0) ? '+' : '-';
168 + hours = abs(hours);
169 +
170 + // Hours with leading zero
171 + pos += print_2digit(&buffer[pos], len - pos, hours);
172 +
173 + // Colon
174 + buffer[pos++] = ':';
175 +
176 + // Minutes with leading zero
177 + pos += print_2digit(&buffer[pos], len - pos, minutes);
178 }
179 }
180
60 - return used_length;
181 +finish:
182 + // Ensure null termination
183 + if (pos < len)
184 + buffer[pos] = '\0';
185 + else
186 + buffer[len - 1] = '\0';
187 +
188 + return pos;
189 }
190
191 usec_t rfc3339_parse_ut(const char *rfc3339, char **endptr) {