summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEmanuele Torre <torreemanuele6@gmail.com>2024-03-19 02:01:53 +0100
committerGitHub <noreply@github.com>2024-03-19 02:01:53 +0100
commitbc9614682571fa2e8185398c89322c0300f92f76 (patch)
treed0cd4425341e1597e906824ad9847ca18ca25fcd
parentd69733154a2b411c4248a3f90e582115a8512276 (diff)
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
-rw-r--r--src/builtin.c50
-rw-r--r--tests/jq.test4
2 files changed, 32 insertions, 22 deletions
diff --git a/src/builtin.c b/src/builtin.c
index 5e31795c..05bd8592 100644
--- a/src/builtin.c
+++ b/src/builtin.c
@@ -1461,30 +1461,35 @@ static jv f_strptime(jq_state *jq, jv a, jv b) {
return r;
}
-#define TO_TM_FIELD(t, j, i) \
- do { \
- jv n = jv_array_get(jv_copy(j), (i)); \
- if (jv_get_kind(n) != (JV_KIND_NUMBER)) { \
- jv_free(n); \
- jv_free(j); \
- return 0; \
- } \
- t = jv_number_value(n); \
- jv_free(n); \
- } while (0)
-
static int jv2tm(jv a, struct tm *tm) {
memset(tm, 0, sizeof(*tm));
- TO_TM_FIELD(tm->tm_year, a, 0);
- tm->tm_year -= 1900;
- TO_TM_FIELD(tm->tm_mon, a, 1);
- TO_TM_FIELD(tm->tm_mday, a, 2);
- TO_TM_FIELD(tm->tm_hour, a, 3);
- TO_TM_FIELD(tm->tm_min, a, 4);
- TO_TM_FIELD(tm->tm_sec, a, 5);
- TO_TM_FIELD(tm->tm_wday, a, 6);
- TO_TM_FIELD(tm->tm_yday, a, 7);
- jv_free(a);
+ static size_t offsets[] = {
+ offsetof(struct tm, tm_year),
+ offsetof(struct tm, tm_mon),
+ offsetof(struct tm, tm_mday),
+ offsetof(struct tm, tm_hour),
+ offsetof(struct tm, tm_min),
+ offsetof(struct tm, tm_sec),
+ offsetof(struct tm, tm_wday),
+ offsetof(struct tm, tm_yday),
+ };
+
+ for (size_t i = 0; i < (sizeof offsets / sizeof *offsets); ++i) {
+ jv n = jv_array_get(jv_copy(a), i);
+ if (!jv_is_valid(n))
+ break;
+ if (jv_get_kind(n) != JV_KIND_NUMBER || jvp_number_is_nan(n)) {
+ jv_free(a);
+ jv_free(n);
+ return 0;
+ }
+ double d = jv_number_value(n);
+ if (i == 0) /* year */
+ d -= 1900;
+ *(int *)((void *)tm + offsets[i]) = d < INT_MIN ? INT_MIN :
+ d > INT_MAX ? INT_MAX : (int)d;
+ jv_free(n);
+ }
// We use UTC everywhere (gettimeofday, gmtime) and UTC does not do DST.
// Setting tm_isdst to 0 is done by the memset.
@@ -1494,6 +1499,7 @@ static int jv2tm(jv a, struct tm *tm) {
// hope it is okay to initialize them to zero, because the standard does not
// provide an alternative.
+ jv_free(a);
return 1;
}
diff --git a/tests/jq.test b/tests/jq.test
index 463161c7..584ab2b6 100644
--- a/tests/jq.test
+++ b/tests/jq.test
@@ -1568,6 +1568,10 @@ strftime("%A, %B %d, %Y")
1435677542.822351
"Tuesday, June 30, 2015"
+strftime("%Y-%m-%dT%H:%M:%SZ")
+[2024,2,15]
+"2024-03-15T00:00:00Z"
+
gmtime
1425599507
[2015,2,5,23,51,47,4,63]