summaryrefslogtreecommitdiffstats
path: root/src
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 /src
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.
Diffstat (limited to 'src')
-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)
98 return n; 98 return n;
99} 99}
100#undef PO10u_LIMIT 100#undef PO10u_LIMIT
101
102int plist_real_to_time64(double realval, Time64_T *timev)
103{
104 if (!timev || !isfinite(realval)) {
105 return -1;
106 }
107
108 if (realval < (double)TIME64_MIN - (double)MAC_EPOCH ||
109 realval > (double)TIME64_MAX - (double)MAC_EPOCH) {
110 return -1;
111 }
112
113 *timev = (Time64_T)realval + MAC_EPOCH;
114 return 0;
115}
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 @@
22#define COMMON_H 22#define COMMON_H
23 23
24#include <stddef.h> 24#include <stddef.h>
25#include "time64.h"
25 26
26#define MAC_EPOCH 978307200 27#define MAC_EPOCH 978307200
27 28
28size_t dtostr(char *buf, size_t bufsize, double realval); 29size_t dtostr(char *buf, size_t bufsize, double realval);
29int num_digits_i(int64_t i); 30int num_digits_i(int64_t i);
30int num_digits_u(uint64_t i); 31int num_digits_u(uint64_t i);
32int plist_real_to_time64(double realval, Time64_T *timev);
31 33
32#endif 34#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
257 break; 257 break;
258 case PLIST_DATE: 258 case PLIST_DATE:
259 if (coerce) { 259 if (coerce) {
260 Time64_T timev = (Time64_T)node_data->realval + MAC_EPOCH; 260 Time64_T timev;
261 if (plist_real_to_time64(node_data->realval, &timev) < 0) {
262 return PLIST_ERR_INVALID_ARG;
263 }
261 struct TM _btime; 264 struct TM _btime;
262 struct TM *btime = gmtime64_r(&timev, &_btime); 265 struct TM *btime = gmtime64_r(&timev, &_btime);
263 char datebuf[32]; 266 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
301 break; 301 break;
302 case PLIST_DATE: 302 case PLIST_DATE:
303 if (coerce) { 303 if (coerce) {
304 Time64_T timev = (Time64_T)node_data->realval + MAC_EPOCH; 304 Time64_T timev;
305 if (plist_real_to_time64(node_data->realval, &timev) < 0) {
306 return PLIST_ERR_INVALID_ARG;
307 }
305 struct TM _btime; 308 struct TM _btime;
306 struct TM *btime = gmtime64_r(&timev, &_btime); 309 struct TM *btime = gmtime64_r(&timev, &_btime);
307 char datebuf[32]; 310 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
231 break; 231 break;
232 case PLIST_DATE: 232 case PLIST_DATE:
233 { 233 {
234 Time64_T timev = (Time64_T)node_data->realval + MAC_EPOCH; 234 Time64_T timev;
235 if (plist_real_to_time64(node_data->realval, &timev) < 0) {
236 return PLIST_ERR_INVALID_ARG;
237 }
235 struct TM _btime; 238 struct TM _btime;
236 struct TM *btime = gmtime64_r(&timev, &_btime); 239 struct TM *btime = gmtime64_r(&timev, &_btime);
237 if (btime) { 240 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
220 break; 220 break;
221 case PLIST_DATE: 221 case PLIST_DATE:
222 { 222 {
223 Time64_T timev = (Time64_T)node_data->realval + MAC_EPOCH; 223 Time64_T timev;
224 if (plist_real_to_time64(node_data->realval, &timev) < 0) {
225 return PLIST_ERR_INVALID_ARG;
226 }
224 struct TM _btime; 227 struct TM _btime;
225 struct TM *btime = gmtime64_r(&timev, &_btime); 228 struct TM *btime = gmtime64_r(&timev, &_btime);
226 if (btime) { 229 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
237 break; 237 break;
238 case PLIST_DATE: 238 case PLIST_DATE:
239 { 239 {
240 Time64_T timev = (Time64_T)node_data->realval + MAC_EPOCH; 240 Time64_T timev;
241 if (plist_real_to_time64(node_data->realval, &timev) < 0) {
242 return PLIST_ERR_INVALID_ARG;
243 }
241 struct TM _btime; 244 struct TM _btime;
242 struct TM *btime = gmtime64_r(&timev, &_btime); 245 struct TM *btime = gmtime64_r(&timev, &_btime);
243 if (btime) { 246 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;
11typedef Int64 Time64_T; 11typedef Int64 Time64_T;
12typedef Int64 Year; 12typedef Int64 Year;
13 13
14#ifndef TIME64_MIN
15#define TIME64_MIN ((Time64_T)INT64_MIN)
16#endif
17
18#ifndef TIME64_MAX
19#define TIME64_MAX ((Time64_T)INT64_MAX)
20#endif
14 21
15/* A copy of the tm struct but with a 64 bit year */ 22/* A copy of the tm struct but with a 64 bit year */
16struct TM64 { 23struct 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
192 tag = XPLIST_DATE; 192 tag = XPLIST_DATE;
193 tag_len = XPLIST_DATE_LEN; 193 tag_len = XPLIST_DATE_LEN;
194 { 194 {
195 Time64_T timev = (Time64_T)node_data->realval + MAC_EPOCH; 195 Time64_T timev;
196 if (plist_real_to_time64(node_data->realval, &timev) < 0) {
197 return PLIST_ERR_INVALID_ARG;
198 }
196 struct TM _btime; 199 struct TM _btime;
197 struct TM *btime = gmtime64_r(&timev, &_btime); 200 struct TM *btime = gmtime64_r(&timev, &_btime);
198 if (btime) { 201 if (btime) {