Skip to content

Commit 5f61b1f

Browse files
committed
builtin.c: jv2tm: fix UB and accept array inputs with not all the values
Now, time functions accept array inputs even if they don't have all the elements, 0 will be assumed if a value is not present. Also, jv2tm now properly clamps large number values to a signed 32-bit integer and rejects nan. Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=65885
1 parent 1411ce6 commit 5f61b1f

File tree

2 files changed

+32
-22
lines changed

2 files changed

+32
-22
lines changed

src/builtin.c

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1461,30 +1461,35 @@ static jv f_strptime(jq_state *jq, jv a, jv b) {
14611461
return r;
14621462
}
14631463

1464-
#define TO_TM_FIELD(t, j, i) \
1465-
do { \
1466-
jv n = jv_array_get(jv_copy(j), (i)); \
1467-
if (jv_get_kind(n) != (JV_KIND_NUMBER)) { \
1468-
jv_free(n); \
1469-
jv_free(j); \
1470-
return 0; \
1471-
} \
1472-
t = jv_number_value(n); \
1473-
jv_free(n); \
1474-
} while (0)
1475-
14761464
static int jv2tm(jv a, struct tm *tm) {
14771465
memset(tm, 0, sizeof(*tm));
1478-
TO_TM_FIELD(tm->tm_year, a, 0);
1479-
tm->tm_year -= 1900;
1480-
TO_TM_FIELD(tm->tm_mon, a, 1);
1481-
TO_TM_FIELD(tm->tm_mday, a, 2);
1482-
TO_TM_FIELD(tm->tm_hour, a, 3);
1483-
TO_TM_FIELD(tm->tm_min, a, 4);
1484-
TO_TM_FIELD(tm->tm_sec, a, 5);
1485-
TO_TM_FIELD(tm->tm_wday, a, 6);
1486-
TO_TM_FIELD(tm->tm_yday, a, 7);
1487-
jv_free(a);
1466+
static size_t offsets[] = {
1467+
offsetof(struct tm, tm_year),
1468+
offsetof(struct tm, tm_mon),
1469+
offsetof(struct tm, tm_mday),
1470+
offsetof(struct tm, tm_hour),
1471+
offsetof(struct tm, tm_min),
1472+
offsetof(struct tm, tm_sec),
1473+
offsetof(struct tm, tm_wday),
1474+
offsetof(struct tm, tm_yday),
1475+
};
1476+
1477+
for (size_t i = 0; i < (sizeof offsets / sizeof *offsets); ++i) {
1478+
jv n = jv_array_get(jv_copy(a), i);
1479+
if (!jv_is_valid(n))
1480+
break;
1481+
if (jv_get_kind(n) != JV_KIND_NUMBER || jvp_number_is_nan(n)) {
1482+
jv_free(a);
1483+
jv_free(n);
1484+
return 0;
1485+
}
1486+
double d = jv_number_value(n);
1487+
jv_free(n);
1488+
if (i == 0) /* year */
1489+
d -= 1900;
1490+
*(int *)((void *)tm + offsets[i]) = d < INT_MIN ? INT_MIN :
1491+
d > INT_MAX ? INT_MAX : (int)d;
1492+
}
14881493

14891494
// We use UTC everywhere (gettimeofday, gmtime) and UTC does not do DST.
14901495
// Setting tm_isdst to 0 is done by the memset.
@@ -1494,6 +1499,7 @@ static int jv2tm(jv a, struct tm *tm) {
14941499
// hope it is okay to initialize them to zero, because the standard does not
14951500
// provide an alternative.
14961501

1502+
jv_free(a);
14971503
return 1;
14981504
}
14991505

tests/jq.test

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1568,6 +1568,10 @@ strftime("%A, %B %d, %Y")
15681568
1435677542.822351
15691569
"Tuesday, June 30, 2015"
15701570

1571+
try ["OK", strftime("%Y-%m-%dT%H:%M:%SZ")] catch ["KO", .]
1572+
[2024]
1573+
["OK","2024-01-00T00:00:00Z"]
1574+
15711575
gmtime
15721576
1425599507
15731577
[2015,2,5,23,51,47,4,63]

0 commit comments

Comments
 (0)