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) {