<feed xmlns='http://www.w3.org/2005/Atom'>
<title>libplist, branch 2.8.1</title>
<subtitle>Library to handle Apple Property List format files in binary or XML</subtitle>
<link rel='alternate' type='text/html' href='https://cgit.sukimashita.com/libplist.git/'/>
<entry>
<title>Update NEWS for release</title>
<updated>2026-11-06T11:39:38+00:00</updated>
<author>
<name>Nikias Bassen</name>
</author>
<published>2026-11-06T11:39:38+00:00</published>
<link rel='alternate' type='text/html' href='https://cgit.sukimashita.com/libplist.git/commit/?id=678b1a34693b86ae1787fa470dcb7203ffd0ef3f'/>
<id>678b1a34693b86ae1787fa470dcb7203ffd0ef3f</id>
<content type='text'>
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
</pre>
</div>
</content>
</entry>
<entry>
<title>Bump soversion for release</title>
<updated>2026-11-06T11:39:25+00:00</updated>
<author>
<name>Nikias Bassen</name>
</author>
<published>2026-11-06T11:39:25+00:00</published>
<link rel='alternate' type='text/html' href='https://cgit.sukimashita.com/libplist.git/commit/?id=6b6b9d622e67292983b55b3ad35e55bb34ed6e16'/>
<id>6b6b9d622e67292983b55b3ad35e55bb34ed6e16</id>
<content type='text'>
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
</pre>
</div>
</content>
</entry>
<entry>
<title>plist: treat NaN date values as 0 in plist_get_date_val</title>
<updated>2026-11-06T09:32:42+00:00</updated>
<author>
<name>Nikias Bassen</name>
</author>
<published>2026-11-06T09:32:42+00:00</published>
<link rel='alternate' type='text/html' href='https://cgit.sukimashita.com/libplist.git/commit/?id=a3215e2a6577df39d944578af671f9dae2770336'/>
<id>a3215e2a6577df39d944578af671f9dae2770336</id>
<content type='text'>
Converting NaN to int32_t is undefined behavior, and NaN can come from
a parsed date node.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Converting NaN to int32_t is undefined behavior, and NaN can come from
a parsed date node.
</pre>
</div>
</content>
</entry>
<entry>
<title>plist: fix the (sec, usec) date API round trip</title>
<updated>2026-11-06T09:29:06+00:00</updated>
<author>
<name>Flavio Toffalini</name>
</author>
<published>2026-10-26T09:03:57+00:00</published>
<link rel='alternate' type='text/html' href='https://cgit.sukimashita.com/libplist.git/commit/?id=1d3b54c784da260f71d885f5f560d69c211a645e'/>
<id>1d3b54c784da260f71d885f5f560d69c211a645e</id>
<content type='text'>
plist_set_date_val stores val = sec + usec / 1000000, but
plist_get_date_val splits it with a truncating cast and fabs():

    *sec  = (int32_t)val;
    val   = fabs((val - (int64_t)val) * 1000000);
    *usec = (int32_t)val;

Two results are wrong. The product is truncated, so set(5, 6) reads
back as (5, 5) - 29.3% of round trips over sec in [0, 3] come back a
microsecond short. And for a negative value, truncation toward zero
plus fabs() give a pair that does not denote the same instant:
set(-3, 250000) stores -2.75 and reads back as (-2, 750000), which the
setter would store as -1.25. Every round trip with sec &lt; 0 and
usec != 0 is wrong.

(int32_t)val is also undefined when the stored value is outside the
int32_t range.

Round the seconds down, round the fraction to microseconds carrying
1000000 into the seconds, and clamp the seconds before converting, so
sec + usec / 1000000 == val to the microsecond with 0 &lt;= usec &lt;
1000000 - the convention the setter uses.

Add test/date_val.c covering fixed cases and a sweep over sec in
[-2, 2]. It fails on an unmodified tree - set(5, 6), set(-3, 250000)
and set(-1, 1) all come back as different instants - and passes with
this change, with make check at 44/44.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01Arn4YaWtcQBDsQuJtyBmFP
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
plist_set_date_val stores val = sec + usec / 1000000, but
plist_get_date_val splits it with a truncating cast and fabs():

    *sec  = (int32_t)val;
    val   = fabs((val - (int64_t)val) * 1000000);
    *usec = (int32_t)val;

Two results are wrong. The product is truncated, so set(5, 6) reads
back as (5, 5) - 29.3% of round trips over sec in [0, 3] come back a
microsecond short. And for a negative value, truncation toward zero
plus fabs() give a pair that does not denote the same instant:
set(-3, 250000) stores -2.75 and reads back as (-2, 750000), which the
setter would store as -1.25. Every round trip with sec &lt; 0 and
usec != 0 is wrong.

(int32_t)val is also undefined when the stored value is outside the
int32_t range.

Round the seconds down, round the fraction to microseconds carrying
1000000 into the seconds, and clamp the seconds before converting, so
sec + usec / 1000000 == val to the microsecond with 0 &lt;= usec &lt;
1000000 - the convention the setter uses.

Add test/date_val.c covering fixed cases and a sweep over sec in
[-2, 2]. It fails on an unmodified tree - set(5, 6), set(-3, 250000)
and set(-1, 1) all come back as different instants - and passes with
this change, with make check at 44/44.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01Arn4YaWtcQBDsQuJtyBmFP
</pre>
</div>
</content>
</entry>
<entry>
<title>bplist: Guard uint64_mul_overflow fallback against division by zero</title>
<updated>2026-11-06T08:39:42+00:00</updated>
<author>
<name>Nikias Bassen</name>
</author>
<published>2026-11-06T08:39:42+00:00</published>
<link rel='alternate' type='text/html' href='https://cgit.sukimashita.com/libplist.git/commit/?id=9fdbf585f47501ba19a53fd4bb382d5b533e3fdd'/>
<id>9fdbf585f47501ba19a53fd4bb382d5b533e3fdd</id>
<content type='text'>
The portable fallback used when __builtin_umulll_overflow is not
available computed a &gt; UINT64_MAX / b, which divides by zero if b is 0.
No current caller passes 0, but return no overflow in that case so the
helper is safe for any input.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
The portable fallback used when __builtin_umulll_overflow is not
available computed a &gt; UINT64_MAX / b, which divides by zero if b is 0.
No current caller passes 0, but return no overflow in that case so the
helper is safe for any input.
</pre>
</div>
</content>
</entry>
<entry>
<title>test: Add binary plist parser behavior tests</title>
<updated>2026-11-05T22:46:59+00:00</updated>
<author>
<name>Nathan Chandrasekar</name>
</author>
<published>2026-10-18T00:14:17+00:00</published>
<link rel='alternate' type='text/html' href='https://cgit.sukimashita.com/libplist.git/commit/?id=70a64c23c9d77b114dcc71e70e69aa8868693f3f'/>
<id>70a64c23c9d77b114dcc71e70e69aa8868693f3f</id>
<content type='text'>
Build small binary plists in memory and check that arrays and dicts
parse correctly with 1, 2 and 3 byte object references, and that
strings, extended size markers and array/dict references extending
beyond the object data area into the offset table are rejected.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Build small binary plists in memory and check that arrays and dicts
parse correctly with 1, 2 and 3 byte object references, and that
strings, extended size markers and array/dict references extending
beyond the object data area into the offset table are rejected.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>bplist: Use length-based range checks for object data</title>
<updated>2026-11-05T22:46:59+00:00</updated>
<author>
<name>Nathan Chandrasekar</name>
</author>
<published>2026-10-18T00:14:17+00:00</published>
<link rel='alternate' type='text/html' href='https://cgit.sukimashita.com/libplist.git/commit/?id=86def304f2915a13170bd0ff7adf0816ab3178a6'/>
<id>86def304f2915a13170bd0ff7adf0816ab3178a6</id>
<content type='text'>
The binary plist parser validated object data by computing pointers such
as `object + size` or `refs + j * ref_size` and comparing them against the
offset table. Each of these checks had to be written with its own
wrap-around handling, and the pointer was formed before it was known to
be inside the input buffer.

Add bplist_object_range_valid(), which checks a length against the bytes
remaining before the offset table, and bplist_read_object_ref(), which
reads an array/dict object reference with an overflow-checked offset.
Use them for all object data checks in parse_bin_node() and for the
reference reads in parse_array_node() and parse_dict_node(). Arrays and
dicts now validate the full reference list (count * ref_size, or twice
that for dicts) up front instead of only the item count.

The node offset checks in parse_bin_node_at_index() and the offset table
check in plist_from_bin() compare offsets instead of pointers as well.

Valid binary plists are parsed exactly as before.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
The binary plist parser validated object data by computing pointers such
as `object + size` or `refs + j * ref_size` and comparing them against the
offset table. Each of these checks had to be written with its own
wrap-around handling, and the pointer was formed before it was known to
be inside the input buffer.

Add bplist_object_range_valid(), which checks a length against the bytes
remaining before the offset table, and bplist_read_object_ref(), which
reads an array/dict object reference with an overflow-checked offset.
Use them for all object data checks in parse_bin_node() and for the
reference reads in parse_array_node() and parse_dict_node(). Arrays and
dicts now validate the full reference list (count * ref_size, or twice
that for dicts) up front instead of only the item count.

The node offset checks in parse_bin_node_at_index() and the offset table
check in plist_from_bin() compare offsets instead of pointers as well.

Valid binary plists are parsed exactly as before.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>oplist: detect errors when parsing 'strings' data</title>
<updated>2026-11-05T11:02:11+00:00</updated>
<author>
<name>Nikias Bassen</name>
</author>
<published>2026-11-05T11:02:11+00:00</published>
<link rel='alternate' type='text/html' href='https://cgit.sukimashita.com/libplist.git/commit/?id=9d238e5e1a55a126c8d19d644a5a2a9c305eb564'/>
<id>9d238e5e1a55a126c8d19d644a5a2a9c305eb564</id>
<content type='text'>
plist_from_openstep() tested ctx.err &gt; 0 after parsing the plist as
'strings' data, but error codes are negative, so a failure was never
detected and the function returned success with a NULL plist (leaking the
temporary dict). Check for any error and return it.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
plist_from_openstep() tested ctx.err &gt; 0 after parsing the plist as
'strings' data, but error codes are negative, so a failure was never
detected and the function returned success with a NULL plist (leaking the
temporary dict). Check for any error and return it.
</pre>
</div>
</content>
</entry>
<entry>
<title>Fix leaks and crashes on allocation failure</title>
<updated>2026-11-05T11:02:05+00:00</updated>
<author>
<name>Nikias Bassen</name>
</author>
<published>2026-11-05T11:02:05+00:00</published>
<link rel='alternate' type='text/html' href='https://cgit.sukimashita.com/libplist.git/commit/?id=9fd3406baec5997d386cdb4c7492117604ba0a70'/>
<id>9fd3406baec5997d386cdb4c7492117604ba0a70</id>
<content type='text'>
Found by failing each allocation in turn under ASan/UBSan/LSan:

- plist_free() leaked the whole tree if its work stack could not be
  allocated; free the children iteratively without extra memory
- plist_copy(): when a shallow copy of a dict/array failed, the original's
  lookup table was freed along with the copy's data (use-after-free)
- bplist parser: fix a NULL dereference in parse_date_node(), check the
  node_attach() results, and don't leak on failure; plist_to_bin() no
  longer leaks on an allocation failure
- free the plist data when node creation fails (plist constructors, XML,
  JSON and binary parsers)
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Found by failing each allocation in turn under ASan/UBSan/LSan:

- plist_free() leaked the whole tree if its work stack could not be
  allocated; free the children iteratively without extra memory
- plist_copy(): when a shallow copy of a dict/array failed, the original's
  lookup table was freed along with the copy's data (use-after-free)
- bplist parser: fix a NULL dereference in parse_date_node(), check the
  node_attach() results, and don't leak on failure; plist_to_bin() no
  longer leaks on an allocation failure
- free the plist data when node creation fails (plist constructors, XML,
  JSON and binary parsers)
</pre>
</div>
</content>
</entry>
<entry>
<title>Harden allocation failure handling and size arithmetic</title>
<updated>2026-11-05T11:01:59+00:00</updated>
<author>
<name>Nikias Bassen</name>
</author>
<published>2026-11-05T11:01:59+00:00</published>
<link rel='alternate' type='text/html' href='https://cgit.sukimashita.com/libplist.git/commit/?id=4dc93bc274867b42aa82fc91ee4d5878d23a3f36'/>
<id>4dc93bc274867b42aa82fc91ee4d5878d23a3f36</id>
<content type='text'>
- check malloc/calloc/realloc results in constructors, the JSON and
  OpenStep writers, the XML text-part helpers and base64 decoding, and
  return PLIST_ERR_NO_MEM instead of asserting or dereferencing NULL
- guard size calculations against overflow in the UTF-8/UTF-16BE
  conversions, ptrarray, and bytearray
- make hash_table_insert() report failure instead of silently dropping
  the entry; writers propagate PLIST_ERR_NO_MEM, and dict lookup caches
  are discarded so lookups fall back to a linear search
- don't overwrite an earlier parse error code in the XML parser, and
  report PLIST_ERR_NO_MEM when reading text content fails for lack of memory
- OpenStep parser: check node/data/string allocations and dict/array
  insertion results

Based on #323 by bg0d-droid.

Co-authored-by: bg0d-droid &lt;81825610+bg0d-droid@users.noreply.github.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
- check malloc/calloc/realloc results in constructors, the JSON and
  OpenStep writers, the XML text-part helpers and base64 decoding, and
  return PLIST_ERR_NO_MEM instead of asserting or dereferencing NULL
- guard size calculations against overflow in the UTF-8/UTF-16BE
  conversions, ptrarray, and bytearray
- make hash_table_insert() report failure instead of silently dropping
  the entry; writers propagate PLIST_ERR_NO_MEM, and dict lookup caches
  are discarded so lookups fall back to a linear search
- don't overwrite an earlier parse error code in the XML parser, and
  report PLIST_ERR_NO_MEM when reading text content fails for lack of memory
- OpenStep parser: check node/data/string allocations and dict/array
  insertion results

Based on #323 by bg0d-droid.

Co-authored-by: bg0d-droid &lt;81825610+bg0d-droid@users.noreply.github.com&gt;
</pre>
</div>
</content>
</entry>
</feed>
