File: | build-scan/../src/journal/journald-rate-limit.c |
Warning: | line 98, column 17 Use of memory after it is freed |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* SPDX-License-Identifier: LGPL-2.1+ */ | |||
2 | ||||
3 | #include <errno(*__errno_location ()).h> | |||
4 | #include <string.h> | |||
5 | ||||
6 | #include "alloc-util.h" | |||
7 | #include "hashmap.h" | |||
8 | #include "journald-rate-limit.h" | |||
9 | #include "list.h" | |||
10 | #include "random-util.h" | |||
11 | #include "string-util.h" | |||
12 | #include "util.h" | |||
13 | ||||
14 | #define POOLS_MAX5 5 | |||
15 | #define BUCKETS_MAX127 127 | |||
16 | #define GROUPS_MAX2047 2047 | |||
17 | ||||
18 | static const int priority_map[] = { | |||
19 | [LOG_EMERG0] = 0, | |||
20 | [LOG_ALERT1] = 0, | |||
21 | [LOG_CRIT2] = 0, | |||
22 | [LOG_ERR3] = 1, | |||
23 | [LOG_WARNING4] = 2, | |||
24 | [LOG_NOTICE5] = 3, | |||
25 | [LOG_INFO6] = 3, | |||
26 | [LOG_DEBUG7] = 4 | |||
27 | }; | |||
28 | ||||
29 | typedef struct JournalRateLimitPool JournalRateLimitPool; | |||
30 | typedef struct JournalRateLimitGroup JournalRateLimitGroup; | |||
31 | ||||
32 | struct JournalRateLimitPool { | |||
33 | usec_t begin; | |||
34 | unsigned num; | |||
35 | unsigned suppressed; | |||
36 | }; | |||
37 | ||||
38 | struct JournalRateLimitGroup { | |||
39 | JournalRateLimit *parent; | |||
40 | ||||
41 | char *id; | |||
42 | ||||
43 | /* Interval is stored to keep track of when the group expires */ | |||
44 | usec_t interval; | |||
45 | ||||
46 | JournalRateLimitPool pools[POOLS_MAX5]; | |||
47 | uint64_t hash; | |||
48 | ||||
49 | LIST_FIELDS(JournalRateLimitGroup, bucket)JournalRateLimitGroup *bucket_next, *bucket_prev; | |||
50 | LIST_FIELDS(JournalRateLimitGroup, lru)JournalRateLimitGroup *lru_next, *lru_prev; | |||
51 | }; | |||
52 | ||||
53 | struct JournalRateLimit { | |||
54 | ||||
55 | JournalRateLimitGroup* buckets[BUCKETS_MAX127]; | |||
56 | JournalRateLimitGroup *lru, *lru_tail; | |||
57 | ||||
58 | unsigned n_groups; | |||
59 | ||||
60 | uint8_t hash_key[16]; | |||
61 | }; | |||
62 | ||||
63 | JournalRateLimit *journal_rate_limit_new(void) { | |||
64 | JournalRateLimit *r; | |||
65 | ||||
66 | r = new0(JournalRateLimit, 1)((JournalRateLimit*) calloc((1), sizeof(JournalRateLimit))); | |||
67 | if (!r) | |||
68 | return NULL((void*)0); | |||
69 | ||||
70 | random_bytes(r->hash_key, sizeof(r->hash_key)); | |||
71 | ||||
72 | return r; | |||
73 | } | |||
74 | ||||
75 | static void journal_rate_limit_group_free(JournalRateLimitGroup *g) { | |||
76 | assert(g)do { if ((__builtin_expect(!!(!(g)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("g"), "../src/journal/journald-rate-limit.c" , 76, __PRETTY_FUNCTION__); } while (0); | |||
77 | ||||
78 | if (g->parent) { | |||
79 | assert(g->parent->n_groups > 0)do { if ((__builtin_expect(!!(!(g->parent->n_groups > 0)),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("g->parent->n_groups > 0" ), "../src/journal/journald-rate-limit.c", 79, __PRETTY_FUNCTION__ ); } while (0); | |||
80 | ||||
81 | if (g->parent->lru_tail == g) | |||
82 | g->parent->lru_tail = g->lru_prev; | |||
83 | ||||
84 | LIST_REMOVE(lru, g->parent->lru, g)do { typeof(*(g->parent->lru)) **_head = &(g->parent ->lru), *_item = (g); do { if ((__builtin_expect(!!(!(_item )),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("_item"), "../src/journal/journald-rate-limit.c", 84, __PRETTY_FUNCTION__ ); } while (0); if (_item->lru_next) _item->lru_next-> lru_prev = _item->lru_prev; if (_item->lru_prev) _item-> lru_prev->lru_next = _item->lru_next; else { do { if (( __builtin_expect(!!(!(*_head == _item)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("*_head == _item"), "../src/journal/journald-rate-limit.c" , 84, __PRETTY_FUNCTION__); } while (0); *_head = _item->lru_next ; } _item->lru_next = _item->lru_prev = ((void*)0); } while (0); | |||
85 | LIST_REMOVE(bucket, g->parent->buckets[g->hash % BUCKETS_MAX], g)do { typeof(*(g->parent->buckets[g->hash % 127])) ** _head = &(g->parent->buckets[g->hash % 127]), *_item = (g); do { if ((__builtin_expect(!!(!(_item)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("_item"), "../src/journal/journald-rate-limit.c" , 85, __PRETTY_FUNCTION__); } while (0); if (_item->bucket_next ) _item->bucket_next->bucket_prev = _item->bucket_prev ; if (_item->bucket_prev) _item->bucket_prev->bucket_next = _item->bucket_next; else { do { if ((__builtin_expect(! !(!(*_head == _item)),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD , ("*_head == _item"), "../src/journal/journald-rate-limit.c" , 85, __PRETTY_FUNCTION__); } while (0); *_head = _item->bucket_next ; } _item->bucket_next = _item->bucket_prev = ((void*)0 ); } while (0); | |||
86 | ||||
87 | g->parent->n_groups--; | |||
88 | } | |||
89 | ||||
90 | free(g->id); | |||
91 | free(g); | |||
92 | } | |||
93 | ||||
94 | void journal_rate_limit_free(JournalRateLimit *r) { | |||
95 | assert(r)do { if ((__builtin_expect(!!(!(r)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("r"), "../src/journal/journald-rate-limit.c" , 95, __PRETTY_FUNCTION__); } while (0); | |||
| ||||
96 | ||||
97 | while (r->lru) | |||
98 | journal_rate_limit_group_free(r->lru); | |||
| ||||
99 | ||||
100 | free(r); | |||
101 | } | |||
102 | ||||
103 | _pure___attribute__ ((pure)) static bool_Bool journal_rate_limit_group_expired(JournalRateLimitGroup *g, usec_t ts) { | |||
104 | unsigned i; | |||
105 | ||||
106 | assert(g)do { if ((__builtin_expect(!!(!(g)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("g"), "../src/journal/journald-rate-limit.c" , 106, __PRETTY_FUNCTION__); } while (0); | |||
107 | ||||
108 | for (i = 0; i < POOLS_MAX5; i++) | |||
109 | if (g->pools[i].begin + g->interval >= ts) | |||
110 | return false0; | |||
111 | ||||
112 | return true1; | |||
113 | } | |||
114 | ||||
115 | static void journal_rate_limit_vacuum(JournalRateLimit *r, usec_t ts) { | |||
116 | assert(r)do { if ((__builtin_expect(!!(!(r)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("r"), "../src/journal/journald-rate-limit.c" , 116, __PRETTY_FUNCTION__); } while (0); | |||
117 | ||||
118 | /* Makes room for at least one new item, but drop all | |||
119 | * expored items too. */ | |||
120 | ||||
121 | while (r->n_groups >= GROUPS_MAX2047 || | |||
122 | (r->lru_tail && journal_rate_limit_group_expired(r->lru_tail, ts))) | |||
123 | journal_rate_limit_group_free(r->lru_tail); | |||
124 | } | |||
125 | ||||
126 | static JournalRateLimitGroup* journal_rate_limit_group_new(JournalRateLimit *r, const char *id, usec_t interval, usec_t ts) { | |||
127 | JournalRateLimitGroup *g; | |||
128 | struct siphash state; | |||
129 | ||||
130 | assert(r)do { if ((__builtin_expect(!!(!(r)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("r"), "../src/journal/journald-rate-limit.c" , 130, __PRETTY_FUNCTION__); } while (0); | |||
131 | assert(id)do { if ((__builtin_expect(!!(!(id)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("id"), "../src/journal/journald-rate-limit.c" , 131, __PRETTY_FUNCTION__); } while (0); | |||
132 | ||||
133 | g = new0(JournalRateLimitGroup, 1)((JournalRateLimitGroup*) calloc((1), sizeof(JournalRateLimitGroup ))); | |||
134 | if (!g) | |||
135 | return NULL((void*)0); | |||
136 | ||||
137 | g->id = strdup(id); | |||
138 | if (!g->id) | |||
139 | goto fail; | |||
140 | ||||
141 | siphash24_init(&state, r->hash_key); | |||
142 | string_hash_func(g->id, &state); | |||
143 | g->hash = siphash24_finalize(&state); | |||
144 | ||||
145 | g->interval = interval; | |||
146 | ||||
147 | journal_rate_limit_vacuum(r, ts); | |||
148 | ||||
149 | LIST_PREPEND(bucket, r->buckets[g->hash % BUCKETS_MAX], g)do { typeof(*(r->buckets[g->hash % 127])) **_head = & (r->buckets[g->hash % 127]), *_item = (g); do { if ((__builtin_expect (!!(!(_item)),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("_item"), "../src/journal/journald-rate-limit.c", 149, __PRETTY_FUNCTION__ ); } while (0); if ((_item->bucket_next = *_head)) _item-> bucket_next->bucket_prev = _item; _item->bucket_prev = ( (void*)0); *_head = _item; } while (0); | |||
150 | LIST_PREPEND(lru, r->lru, g)do { typeof(*(r->lru)) **_head = &(r->lru), *_item = (g); do { if ((__builtin_expect(!!(!(_item)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("_item"), "../src/journal/journald-rate-limit.c" , 150, __PRETTY_FUNCTION__); } while (0); if ((_item->lru_next = *_head)) _item->lru_next->lru_prev = _item; _item-> lru_prev = ((void*)0); *_head = _item; } while (0); | |||
151 | if (!g->lru_next) | |||
152 | r->lru_tail = g; | |||
153 | r->n_groups++; | |||
154 | ||||
155 | g->parent = r; | |||
156 | return g; | |||
157 | ||||
158 | fail: | |||
159 | journal_rate_limit_group_free(g); | |||
160 | return NULL((void*)0); | |||
161 | } | |||
162 | ||||
163 | static unsigned burst_modulate(unsigned burst, uint64_t available) { | |||
164 | unsigned k; | |||
165 | ||||
166 | /* Modulates the burst rate a bit with the amount of available | |||
167 | * disk space */ | |||
168 | ||||
169 | k = u64log2(available); | |||
170 | ||||
171 | /* 1MB */ | |||
172 | if (k <= 20) | |||
173 | return burst; | |||
174 | ||||
175 | burst = (burst * (k-16)) / 4; | |||
176 | ||||
177 | /* | |||
178 | * Example: | |||
179 | * | |||
180 | * <= 1MB = rate * 1 | |||
181 | * 16MB = rate * 2 | |||
182 | * 256MB = rate * 3 | |||
183 | * 4GB = rate * 4 | |||
184 | * 64GB = rate * 5 | |||
185 | * 1TB = rate * 6 | |||
186 | */ | |||
187 | ||||
188 | return burst; | |||
189 | } | |||
190 | ||||
191 | int journal_rate_limit_test(JournalRateLimit *r, const char *id, usec_t rl_interval, unsigned rl_burst, int priority, uint64_t available) { | |||
192 | uint64_t h; | |||
193 | JournalRateLimitGroup *g; | |||
194 | JournalRateLimitPool *p; | |||
195 | struct siphash state; | |||
196 | unsigned burst; | |||
197 | usec_t ts; | |||
198 | ||||
199 | assert(id)do { if ((__builtin_expect(!!(!(id)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("id"), "../src/journal/journald-rate-limit.c" , 199, __PRETTY_FUNCTION__); } while (0); | |||
200 | ||||
201 | /* Returns: | |||
202 | * | |||
203 | * 0 → the log message shall be suppressed, | |||
204 | * 1 + n → the log message shall be permitted, and n messages were dropped from the peer before | |||
205 | * < 0 → error | |||
206 | */ | |||
207 | ||||
208 | if (!r) | |||
209 | return 1; | |||
210 | ||||
211 | ts = now(CLOCK_MONOTONIC1); | |||
212 | ||||
213 | siphash24_init(&state, r->hash_key); | |||
214 | string_hash_func(id, &state); | |||
215 | h = siphash24_finalize(&state); | |||
216 | g = r->buckets[h % BUCKETS_MAX127]; | |||
217 | ||||
218 | LIST_FOREACH(bucket, g, g)for ((g) = (g); (g); (g) = (g)->bucket_next) | |||
219 | if (streq(g->id, id)(strcmp((g->id),(id)) == 0)) | |||
220 | break; | |||
221 | ||||
222 | if (!g) { | |||
223 | g = journal_rate_limit_group_new(r, id, rl_interval, ts); | |||
224 | if (!g) | |||
225 | return -ENOMEM12; | |||
226 | } else | |||
227 | g->interval = rl_interval; | |||
228 | ||||
229 | if (rl_interval == 0 || rl_burst == 0) | |||
230 | return 1; | |||
231 | ||||
232 | burst = burst_modulate(rl_burst, available); | |||
233 | ||||
234 | p = &g->pools[priority_map[priority]]; | |||
235 | ||||
236 | if (p->begin <= 0) { | |||
237 | p->suppressed = 0; | |||
238 | p->num = 1; | |||
239 | p->begin = ts; | |||
240 | return 1; | |||
241 | } | |||
242 | ||||
243 | if (p->begin + rl_interval < ts) { | |||
244 | unsigned s; | |||
245 | ||||
246 | s = p->suppressed; | |||
247 | p->suppressed = 0; | |||
248 | p->num = 1; | |||
249 | p->begin = ts; | |||
250 | ||||
251 | return 1 + s; | |||
252 | } | |||
253 | ||||
254 | if (p->num < burst) { | |||
255 | p->num++; | |||
256 | return 1; | |||
257 | } | |||
258 | ||||
259 | p->suppressed++; | |||
260 | return 0; | |||
261 | } |