summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2026-05-22 19:20:51 +0200
committerGravatar Nikias Bassen2026-05-22 19:20:51 +0200
commitba82092e43d4769dbc6f0557d58a243f93542486 (patch)
tree4054d7d4b701207f6a7def2799295557c5fdf68d
parent9711459dbed7d60bb00c7d2c052623e8489c88e1 (diff)
downloadlibplist-ba82092e43d4769dbc6f0557d58a243f93542486.tar.gz
libplist-ba82092e43d4769dbc6f0557d58a243f93542486.tar.bz2
common: validate PLIST_DATE values before Time64_T conversion
Avoid undefined behavior when serializing malformed PLIST_DATE values containing NaN, infinity, or values outside the Time64_T range. Add a shared helper for checked date conversion and use it across writer paths.
-rw-r--r--src/common.c15
-rw-r--r--src/common.h2
-rw-r--r--src/jplist.c5
-rw-r--r--src/oplist.c5
-rw-r--r--src/out-default.c5
-rw-r--r--src/out-limd.c5
-rw-r--r--src/out-plutil.c5
-rw-r--r--src/time64.h7
-rw-r--r--src/xplist.c5
9 files changed, 48 insertions, 6 deletions
diff --git a/src/common.c b/src/common.c
index 0b11d57..810c2e0 100644
--- a/src/common.c
+++ b/src/common.c
@@ -98,3 +98,18 @@ int num_digits_u(uint64_t i)
return n;
}
#undef PO10u_LIMIT
+
+int plist_real_to_time64(double realval, Time64_T *timev)
+{
+ if (!timev || !isfinite(realval)) {
+ return -1;
+ }
+
+ if (realval < (double)TIME64_MIN - (double)MAC_EPOCH ||
+ realval > (double)TIME64_MAX - (double)MAC_EPOCH) {
+ return -1;
+ }
+
+ *timev = (Time64_T)realval + MAC_EPOCH;
+ return 0;
+}
diff --git a/src/common.h b/src/common.h
index 7e5aff7..ffaa78c 100644
--- a/src/common.h
+++ b/src/common.h
@@ -22,11 +22,13 @@
#define COMMON_H
#include <stddef.h>
+#include "time64.h"
#define MAC_EPOCH 978307200
size_t dtostr(char *buf, size_t bufsize, double realval);
int num_digits_i(int64_t i);
int num_digits_u(uint64_t i);
+int plist_real_to_time64(double realval, Time64_T *timev);
#endif
diff --git a/src/jplist.c b/src/jplist.c
index cc1cd1b..dd2e36a 100644
--- a/src/jplist.c
+++ b/src/jplist.c
@@ -257,7 +257,10 @@ static plist_err_t node_to_json(node_t node, bytearray_t **outbuf, uint32_t dept
break;
case PLIST_DATE:
if (coerce) {
- Time64_T timev = (Time64_T)node_data->realval + MAC_EPOCH;
+ Time64_T timev;
+ if (plist_real_to_time64(node_data->realval, &timev) < 0) {
+ return PLIST_ERR_INVALID_ARG;
+ }
struct TM _btime;
struct TM *btime = gmtime64_r(&timev, &_btime);
char datebuf[32];
diff --git a/src/oplist.c b/src/oplist.c
index 292467f..4b3b666 100644
--- a/src/oplist.c
+++ b/src/oplist.c
@@ -301,7 +301,10 @@ static plist_err_t node_to_openstep(node_t node, bytearray_t **outbuf, uint32_t
break;
case PLIST_DATE:
if (coerce) {
- Time64_T timev = (Time64_T)node_data->realval + MAC_EPOCH;
+ Time64_T timev;
+ if (plist_real_to_time64(node_data->realval, &timev) < 0) {
+ return PLIST_ERR_INVALID_ARG;
+ }
struct TM _btime;
struct TM *btime = gmtime64_r(&timev, &_btime);
char datebuf[32];
diff --git a/src/out-default.c b/src/out-default.c
index 5d4b6fc..87689d1 100644
--- a/src/out-default.c
+++ b/src/out-default.c
@@ -231,7 +231,10 @@ static plist_err_t node_to_string(node_t node, bytearray_t **outbuf, uint32_t de
break;
case PLIST_DATE:
{
- Time64_T timev = (Time64_T)node_data->realval + MAC_EPOCH;
+ Time64_T timev;
+ if (plist_real_to_time64(node_data->realval, &timev) < 0) {
+ return PLIST_ERR_INVALID_ARG;
+ }
struct TM _btime;
struct TM *btime = gmtime64_r(&timev, &_btime);
if (btime) {
diff --git a/src/out-limd.c b/src/out-limd.c
index b01f966..e47ca0c 100644
--- a/src/out-limd.c
+++ b/src/out-limd.c
@@ -220,7 +220,10 @@ static plist_err_t node_to_string(node_t node, bytearray_t **outbuf, uint32_t de
break;
case PLIST_DATE:
{
- Time64_T timev = (Time64_T)node_data->realval + MAC_EPOCH;
+ Time64_T timev;
+ if (plist_real_to_time64(node_data->realval, &timev) < 0) {
+ return PLIST_ERR_INVALID_ARG;
+ }
struct TM _btime;
struct TM *btime = gmtime64_r(&timev, &_btime);
if (btime) {
diff --git a/src/out-plutil.c b/src/out-plutil.c
index 9f7968e..3b5bd34 100644
--- a/src/out-plutil.c
+++ b/src/out-plutil.c
@@ -237,7 +237,10 @@ static plist_err_t node_to_string(node_t node, bytearray_t **outbuf, uint32_t de
break;
case PLIST_DATE:
{
- Time64_T timev = (Time64_T)node_data->realval + MAC_EPOCH;
+ Time64_T timev;
+ if (plist_real_to_time64(node_data->realval, &timev) < 0) {
+ return PLIST_ERR_INVALID_ARG;
+ }
struct TM _btime;
struct TM *btime = gmtime64_r(&timev, &_btime);
if (btime) {
diff --git a/src/time64.h b/src/time64.h
index 28968c0..2c20ffe 100644
--- a/src/time64.h
+++ b/src/time64.h
@@ -11,6 +11,13 @@ typedef long long Int64;
typedef Int64 Time64_T;
typedef Int64 Year;
+#ifndef TIME64_MIN
+#define TIME64_MIN ((Time64_T)INT64_MIN)
+#endif
+
+#ifndef TIME64_MAX
+#define TIME64_MAX ((Time64_T)INT64_MAX)
+#endif
/* A copy of the tm struct but with a 64 bit year */
struct TM64 {
diff --git a/src/xplist.c b/src/xplist.c
index 8f5cb15..3157ab0 100644
--- a/src/xplist.c
+++ b/src/xplist.c
@@ -192,7 +192,10 @@ static plist_err_t node_to_xml(node_t node, bytearray_t **outbuf, uint32_t depth
tag = XPLIST_DATE;
tag_len = XPLIST_DATE_LEN;
{
- Time64_T timev = (Time64_T)node_data->realval + MAC_EPOCH;
+ Time64_T timev;
+ if (plist_real_to_time64(node_data->realval, &timev) < 0) {
+ return PLIST_ERR_INVALID_ARG;
+ }
struct TM _btime;
struct TM *btime = gmtime64_r(&timev, &_btime);
if (btime) {