<feed xmlns='http://www.w3.org/2005/Atom'>
<title>libplist/src/bplist.c, 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>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>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>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>
<entry>
<title>bplist: limit total parsed nodes to prevent memory exhaustion</title>
<updated>2026-11-05T09:07:22+00:00</updated>
<author>
<name>Nikias Bassen</name>
</author>
<published>2026-11-05T09:07:22+00:00</published>
<link rel='alternate' type='text/html' href='https://cgit.sukimashita.com/libplist.git/commit/?id=2b3cc554270d34ca1e1fae3eb2cbbc740a65bbf3'/>
<id>2b3cc554270d34ca1e1fae3eb2cbbc740a65bbf3</id>
<content type='text'>
Binary plist objects may be referenced multiple times, and each
reference is parsed into a separate node. A tiny crafted input where
arrays reference the same objects repeatedly (without forming a cycle,
so the recursion check does not trigger) expands into millions of
nodes and exhausts memory.

Track the number of nodes parsed and fail with PLIST_ERR_PARSE once it
exceeds num_objects * 16 + 65536. Legitimate files create roughly as
many nodes as they have objects, so they stay well below this limit.

Credit to OSSFuzz
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Binary plist objects may be referenced multiple times, and each
reference is parsed into a separate node. A tiny crafted input where
arrays reference the same objects repeatedly (without forming a cycle,
so the recursion check does not trigger) expands into millions of
nodes and exhausts memory.

Track the number of nodes parsed and fail with PLIST_ERR_PARSE once it
exceeds num_objects * 16 + 65536. Legitimate files create roughly as
many nodes as they have objects, so they stay well below this limit.

Credit to OSSFuzz
</pre>
</div>
</content>
</entry>
<entry>
<title>bytearray: Handle realloc() failure in byte_array_grow()</title>
<updated>2026-09-29T14:39:28+00:00</updated>
<author>
<name>Comtea04</name>
</author>
<published>2026-09-27T06:06:22+00:00</published>
<link rel='alternate' type='text/html' href='https://cgit.sukimashita.com/libplist.git/commit/?id=f168c10b52325b00da6355989a18a11743b33f89'/>
<id>f168c10b52325b00da6355989a18a11743b33f89</id>
<content type='text'>
byte_array_grow() assigned the result of realloc() directly to
ba-&gt;data and increased the capacity even if realloc() failed, so the
following memcpy() in byte_array_append() wrote to NULL + len.

On failure, free the old buffer and leave the byte array in a failed
state (data == NULL), which byte_array_append() already ignores. The
callers check for that state:

- the writers (bin, xml, json, openstep and the text output formats)
  return PLIST_ERR_NO_MEM instead of handing out a NULL/truncated buffer
- node_to_xml() base64-encodes &lt;data&gt; directly into the grown buffer,
  so it must bail out there (only guarded by assert() before)
- the OpenStep parser returns PLIST_ERR_NO_MEM for &lt;hex data&gt; instead of
  silently returning truncated data

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>
byte_array_grow() assigned the result of realloc() directly to
ba-&gt;data and increased the capacity even if realloc() failed, so the
following memcpy() in byte_array_append() wrote to NULL + len.

On failure, free the old buffer and leave the byte array in a failed
state (data == NULL), which byte_array_append() already ignores. The
callers check for that state:

- the writers (bin, xml, json, openstep and the text output formats)
  return PLIST_ERR_NO_MEM instead of handing out a NULL/truncated buffer
- node_to_xml() base64-encodes &lt;data&gt; directly into the grown buffer,
  so it must bail out there (only guarded by assert() before)
- the OpenStep parser returns PLIST_ERR_NO_MEM for &lt;hex data&gt; instead of
  silently returning truncated data

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>ptrarray: Handle realloc() failure in ptr_array_insert()</title>
<updated>2026-09-27T06:06:22+00:00</updated>
<author>
<name>Comtea04</name>
</author>
<published>2026-09-27T06:06:22+00:00</published>
<link rel='alternate' type='text/html' href='https://cgit.sukimashita.com/libplist.git/commit/?id=8b48fa72d359de89215f50cbf6ac8c77ae5de9b4'/>
<id>8b48fa72d359de89215f50cbf6ac8c77ae5de9b4</id>
<content type='text'>
ptr_array_insert() assigned the result of realloc() directly to
pa-&gt;pdata and bumped the capacity without checking for failure, so on
out-of-memory the old buffer was leaked and the following store or
memmove() wrote through a NULL pointer.

Keep the old buffer on failure and return -1 from ptr_array_insert()
and ptr_array_add(). The callers now handle the error:

- the array lookup cache (plist.c) is dropped instead of silently going
  out of sync with the node list; lookups then fall back to walking
  the children, as they do before the cache exists
- bplist parsing (used_indexes) and serialization (objects) return
  PLIST_ERR_NO_MEM; ignoring the error in parse_bin_node_at_index()
  would otherwise loop forever since the array never grows

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>
ptr_array_insert() assigned the result of realloc() directly to
pa-&gt;pdata and bumped the capacity without checking for failure, so on
out-of-memory the old buffer was leaked and the following store or
memmove() wrote through a NULL pointer.

Keep the old buffer on failure and return -1 from ptr_array_insert()
and ptr_array_add(). The callers now handle the error:

- the array lookup cache (plist.c) is dropped instead of silently going
  out of sync with the node list; lookups then fall back to walking
  the children, as they do before the cache exists
- bplist parsing (used_indexes) and serialization (objects) return
  PLIST_ERR_NO_MEM; ignoring the error in parse_bin_node_at_index()
  would otherwise loop forever since the array never grows

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>bplist: Use format macro to use correct format in error message</title>
<updated>2026-05-22T17:23:23+00:00</updated>
<author>
<name>Nikias Bassen</name>
</author>
<published>2026-05-22T17:23:23+00:00</published>
<link rel='alternate' type='text/html' href='https://cgit.sukimashita.com/libplist.git/commit/?id=024864926b1de278d6877a0864c1ca36f35c20e6'/>
<id>024864926b1de278d6877a0864c1ca36f35c20e6</id>
<content type='text'>
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
</pre>
</div>
</content>
</entry>
<entry>
<title>bplist: Add overflow check to node offset pointer arithmetic</title>
<updated>2026-02-12T01:43:50+00:00</updated>
<author>
<name>Nikias Bassen</name>
</author>
<published>2026-02-12T01:43:50+00:00</published>
<link rel='alternate' type='text/html' href='https://cgit.sukimashita.com/libplist.git/commit/?id=9969b8ebeb2dd2ac66e4d18fc15d0340de6e8d0e'/>
<id>9969b8ebeb2dd2ac66e4d18fc15d0340de6e8d0e</id>
<content type='text'>
Credit to OSSFuzz
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Credit to OSSFuzz
</pre>
</div>
</content>
</entry>
<entry>
<title>Add NULL checks across codebase</title>
<updated>2026-02-12T00:20:05+00:00</updated>
<author>
<name>Nikias Bassen</name>
</author>
<published>2026-02-12T00:20:05+00:00</published>
<link rel='alternate' type='text/html' href='https://cgit.sukimashita.com/libplist.git/commit/?id=4e82bc85671cfe50763de2637b54cb8576d7976f'/>
<id>4e82bc85671cfe50763de2637b54cb8576d7976f</id>
<content type='text'>
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
</pre>
</div>
</content>
</entry>
</feed>
