Skip to content

Commit 9c0565b

Browse files
committed
basic/time-util: make parsing of dual_timestamp more strict
*scanf functions set errno on i/o error. For sscanf, this doesn't really apply, so (based on the man page), it seems that errno is unlikely to be ever set to a useful value. So just ignore errno. The error message includes the string that was parsed, so it should be always pretty clear why parsing failed. On the other hand, detect trailing characters and minus prefix that weren't converted properly. This matches what our safe_ato* functions do. Add tests to elucidate various edge cases.
1 parent bf32e38 commit 9c0565b

File tree

2 files changed

+54
-2
lines changed

2 files changed

+54
-2
lines changed

src/basic/time-util.c

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -555,15 +555,29 @@ void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
555555

556556
int dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
557557
uint64_t a, b;
558+
int r, pos;
558559

559560
assert(value);
560561
assert(t);
561562

562-
if (sscanf(value, "%" PRIu64 "%" PRIu64, &a, &b) != 2) {
563-
log_debug("Failed to parse dual timestamp value \"%s\": %m", value);
563+
pos = strspn(value, WHITESPACE);
564+
if (value[pos] == '-')
565+
return -EINVAL;
566+
pos += strspn(value + pos, DIGITS);
567+
pos += strspn(value + pos, WHITESPACE);
568+
if (value[pos] == '-')
569+
return -EINVAL;
570+
571+
r = sscanf(value, "%" PRIu64 "%" PRIu64 "%n", &a, &b, &pos);
572+
if (r != 2) {
573+
log_debug("Failed to parse dual timestamp value \"%s\".", value);
564574
return -EINVAL;
565575
}
566576

577+
if (value[pos] != '\0')
578+
/* trailing garbage */
579+
return -EINVAL;
580+
567581
t->realtime = a;
568582
t->monotonic = b;
569583

src/test/test-time.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,43 @@ static void test_format_timestamp_utc(void) {
273273
test_format_timestamp_utc_one(USEC_INFINITY, NULL);
274274
}
275275

276+
static void test_dual_timestamp_deserialize(void) {
277+
int r;
278+
dual_timestamp t;
279+
280+
r = dual_timestamp_deserialize("1234 5678", &t);
281+
assert_se(r == 0);
282+
assert_se(t.realtime == 1234);
283+
assert_se(t.monotonic == 5678);
284+
285+
r = dual_timestamp_deserialize("1234x 5678", &t);
286+
assert_se(r == -EINVAL);
287+
288+
r = dual_timestamp_deserialize("1234 5678y", &t);
289+
assert_se(r == -EINVAL);
290+
291+
r = dual_timestamp_deserialize("-1234 5678", &t);
292+
assert_se(r == -EINVAL);
293+
294+
r = dual_timestamp_deserialize("1234 -5678", &t);
295+
assert_se(r == -EINVAL);
296+
297+
/* Check that output wasn't modified. */
298+
assert_se(t.realtime == 1234);
299+
assert_se(t.monotonic == 5678);
300+
301+
r = dual_timestamp_deserialize("+123 567", &t);
302+
assert_se(r == 0);
303+
assert_se(t.realtime == 123);
304+
assert_se(t.monotonic == 567);
305+
306+
/* Check that we get "infinity" on overflow. */
307+
r = dual_timestamp_deserialize("18446744073709551617 0", &t);
308+
assert_se(r == 0);
309+
assert_se(t.realtime == USEC_INFINITY);
310+
assert_se(t.monotonic == 0);
311+
}
312+
276313
int main(int argc, char *argv[]) {
277314
uintmax_t x;
278315

@@ -288,6 +325,7 @@ int main(int argc, char *argv[]) {
288325
test_usec_sub();
289326
test_format_timestamp();
290327
test_format_timestamp_utc();
328+
test_dual_timestamp_deserialize();
291329

292330
/* Ensure time_t is signed */
293331
assert_cc((time_t) -1 < (time_t) 1);

0 commit comments

Comments
 (0)