Re: Claude AI code audit of GNUstep core stack — 1 50 fixes, 12 perf optimizations, all available for upstream

Todd White <[email protected]> Mon, 13 Apr 2026 11:37:58 -0400
Newsgroups gmane.comp.lib.gnustep.devel
Message-ID <CAAAC8AJ7zDJubucfjQO1xkfm9+_CO0sHOzCABMo-f9Jbn1vhWg@mail.gmail.com>
--00000000000094bc4e064f5946ab
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

Hi David,

All good feedback - and certainly pick and choose what you wish to take
advantage of / feel is of value.

I will go back and look at the comments you made on RB-1, RB-2, etc. and
reintegrate those questions into the 2nd pass Claude is making through the
code and advise.

Cheers,

Todd


On Mon, Apr 13, 2026 at 11:26=E2=80=AFAM David Chisnall <gnustep@theravensn=
est.org>
wrote:

> Hi,
>
> Looking at the libobjc2 ones:
>
> The issue RB-1 is kind-of real, but the fix is incorrect (we should free
> the object before calling the unexpected exception handler, because it ma=
y
> not return), though this is almost unreachable code.  It can basically
> happen only if there is an internal error in the unwind library.
>
> RB-2 is not correct, selectors being null is undefined behaviour and
> cannot happen in compiler-generated code.  Adding a null check on one of
> the hottest paths in the runtime would be a regression.
>
> TS-7 looks like a fix that we need in a few more places, not sure why it=
=E2=80=99s
> only highlighed in the place the code was copied to, not the place it was
> copied from.
>
> I think TS-14 is spurious, this should not be called twice, the first
> caller nulls out the pointer after the cleanup.  The one corner case wher=
e
> it can be called twice is if cleanup *reallocates* the TLS, in which case
> doing the cleanup twice is correct.  Do you have a test case that
> demonstrates this?
>
> RB-6 looks like the right fix, simple copy-and-paste bug.  Note that this
> happens only when memory is exhausted, at which point most Objective-C
> programs will start failing.
>
> RB-7, the null check is in a silly place (after the dereference), but the
> API contract here is that the argument must not be null, so it=E2=80=99s =
actually
> dead code.  This function also should be setting `*outCount =3D 0` in the
> early returns.
>
> PF-6, yes that refactoring would probably be good to do, though note that
> we don=E2=80=99t hit the weak lock in most cases, only if an object is ma=
rked as
> having weak refs.  Have you measured slowdown from this on anything that
> *isn=E2=80=99t* a contrived microbenchmark?  The quoted slowdown looks in=
credibly
> unlikely unless you have a microbenchmark doing nothing but hitting weak
> references from multiple threads.
>
> PF-7, this will generate exactly the same code unless we explicitly use a
> weaker memory order (both are sequentially consistent by default).  We
> should move this code over to C++11 atomics at some point.
>
> TS-3 is incorrect.  This counter grows monotonically.  If a selector is
> registered *while* this call is happening, then the result is undefined.
> It=E2=80=99s technically UB, in that there is an unsynchronised read.
>
> PF-4 was an intentional design choice.  Method replacements are
> infrequent.  The proposed change would make things worse.
>
> David
>
> On 13 Apr 2026, at 04:35, Todd White <[email protected]> wrote:
>
> Hi GNUstep Team,
>
> As an exercise to test out the latest Claude AI capabilities, we recently
> completed a comprehensive, bottom-up code audit of the GNUstep core stack=
 =E2=80=94
> all seven repositories =E2=80=94 covering libobjc2, libs-base, libs-coreb=
ase,
> libs-opal, libs-quartzcore, libs-gui, and libs-back. The full results,
> documentation, and all fix commits are publicly available at:
>
> https://github.com/DTW-Thalion/gnustep-audit
>
> I wanted to share what we found and offer to contribute any or all of the
> changes back upstream.
>
> ## What we did
>
> We audited the entire stack bottom-up =E2=80=94 runtime through UI layer =
=E2=80=94
> examining every file for robustness issues, thread safety gaps, security
> vulnerabilities, correctness bugs, and performance bottlenecks. Each
> finding was severity-rated, fixed in an atomic commit tagged with a findi=
ng
> ID, and validated with a dedicated regression test. We also wrote 13
> performance benchmarks with a baseline/compare workflow so improvements c=
an
> be measured reproducibly.
>
> ## What we found and fixed
>
> Across all seven repos, we identified and fixed 150 findings:
>
> - 22 Critical =E2=80=94 including NSSecureCoding bypass (class whitelist
> completely unimplemented), TLS server verification disabled by default,
> use-after-free in objc_exception_rethrow, NULL dereferences, data races i=
n
> CFRunLoop and CATransaction, zero thread safety across the entire libs-ba=
ck
> backend (189 files, 0 locks), and a swapped sendto() argument in CFSocket
> that prevented any data from being sent.
>
> - 46 High =E2=80=94 deadlocks in property spinlocks, race conditions, buf=
fer
> overflows (CGContext dash buffer allocated in bytes instead of doubles),
> broken APIs, JSON parser with no recursion depth limit (stack overflow
> DoS), and integer overflow in binary plist bounds checking.
>
> - 61 Medium =E2=80=94 thread safety gaps in GSLayoutManager, NSView, and
> NSApplication event dispatch; missing input validation; and general
> robustness issues.
>
> - 14 Low + 10 confirmed bugs =E2=80=94 documentation issues, minor optimi=
zations,
> swapped arguments, wrong variables, and inverted conditions (e.g., TIFF
> destination init was inverted, making TIFF writing 100% broken).
>
> We also implemented 12 targeted performance optimizations, including:
>
> - 64-way lock striping for weak references (5=E2=80=938=C3=97 concurrent =
throughput)
> - O(1) LRU linked list for NSCache (replacing an O(n) implementation that
> also never evicted)
> - Geometric growth for CFArray (O(n) vs O(n=C2=B2) sequential appends)
> - X11 expose event coalescing, live resize throttling at 60fps, dirty
> region tracking in NSView, DPSimage conversion caching, and stack buffer
> allocation in CFRunLoop to eliminate per-iteration malloc
>
> Benchmark results on MSYS2/ucrt64 show +29=E2=80=9331% for retain/release=
, +12=E2=80=9318%
> for message dispatch, +46=E2=80=9355% for array operations, and +25% for =
NSCache.
>
> ## How the work is organized
>
> Each of the seven repos has its own fork under our GitHub org (
> https://github.com/DTW-Thalion) with fix commits on master. The
> gnustep-audit repo itself contains:
>
> - Per-phase findings reports (docs/phase1 through phase6)
> - A master audit summary (docs/AUDIT-SUMMARY.md)
> - 51 regression tests and 13 benchmarks under instrumentation/
> - A Makefile-driven test and benchmark harness with baseline/compare
> support
>
> All 32 regression tests pass on the patched stack (up from 18/32 on
> unpatched).
>
> ## Offer to contribute upstream
>
> We'd be happy to contribute any or all of these changes back into the mai=
n
> GNUstep repositories =E2=80=94 whether as pull requests, individual patch=
es, or in
> whatever form works best for your workflow. Feel free to help yourself to
> the repo.
>
> The security-critical fixes (NSSecureCoding, TLS defaults, JSON depth
> limit, binary plist overflow) and the confirmed crash bugs (use-after-fre=
e,
> NULL derefs, inverted conditions) are probably the highest-priority
> candidates for upstream integration.
>
> Please feel free to reach out with any questions. We have a lot of respec=
t
> for the GNUstep project (and a bit nostalgic for heady days of
> NeXTStep/OpenStep) and would like to see this work benefit the broader
> community.
>
> It's unclear if the codebase is actively maintained or if many people
> still use it, but we hope that this exercise provides some value.
>
> Best regards,
>
> Todd
>
> *Todd White*
> Managing Director
>
>
> 177 Huntington Avenue, 17th Floor
> Boston, MA 02115
> Telephone: +1 617 237-2835 Ext. 101
> EIN: 33-2228263
>
> Website <https://www.thalion.global/> | Twitter/X
> <https://x.com/TTIScience> | YouTube <https://www.youtube.com/@TTIScience=
>
>
> [image:
> https://app.candid.org/profile/16308928/the-thalion-initiative-us-inc-33-=
2228263/?pkId=3D39bf89e9-f544-478c-a3fe-e157e345181d]
> <https://app.candid.org/profile/16308928/the-thalion-initiative-us-inc/?p=
kId=3D39bf89e9-f544-478c-a3fe-e157e345181d&isActive=3Dtrue>
>
>
>

--00000000000094bc4e064f5946ab
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr"><div class=3D"gmail_default" style=3D"font-family:arial,sa=
ns-serif">Hi David,</div><div class=3D"gmail_default" style=3D"font-family:=
arial,sans-serif"><br></div><div class=3D"gmail_default" style=3D"font-fami=
ly:arial,sans-serif">All good feedback - and certainly pick and choose what=
 you wish to take advantage of / feel is of value.=C2=A0</div><div class=3D=
"gmail_default" style=3D"font-family:arial,sans-serif"><br></div><div class=
=3D"gmail_default" style=3D"font-family:arial,sans-serif">I will go back an=
d look at the comments you made on RB-1, RB-2, etc. and reintegrate those q=
uestions into the 2nd pass Claude is making through the code and advise.</d=
iv><div class=3D"gmail_default" style=3D"font-family:arial,sans-serif"><br>=
</div><div class=3D"gmail_default" style=3D"font-family:arial,sans-serif">C=
heers,</div><div class=3D"gmail_default" style=3D"font-family:arial,sans-se=
rif"><br></div><div class=3D"gmail_default" style=3D"font-family:arial,sans=
-serif">Todd</div><div class=3D"gmail_default" style=3D"font-family:arial,s=
ans-serif"><br></div></div><br><div class=3D"gmail_quote gmail_quote_contai=
ner"><div dir=3D"ltr" class=3D"gmail_attr">On Mon, Apr 13, 2026 at 11:26=E2=
=80=AFAM David Chisnall &lt;<a href=3D"mailto:[email protected]">gn=
[email protected]</a>&gt; wrote:<br></div><blockquote class=3D"gmail_=
quote" style=3D"margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,=
204);padding-left:1ex"><div>Hi,<div><br></div><div>Looking at the libobjc2 =
ones:</div><div><br></div><div>The issue RB-1 is kind-of real, but the fix =
is incorrect (we should free the object before calling the unexpected excep=
tion handler, because it may not return), though this is almost unreachable=
 code.=C2=A0 It can basically happen only if there is an internal error in =
the unwind library.</div><div><br></div><div>RB-2 is not correct, selectors=
 being null is undefined behaviour and cannot happen in compiler-generated =
code.=C2=A0 Adding a null check on one of the hottest paths in the runtime =
would be a regression.</div><div><br></div><div>TS-7 looks like a fix that =
we need in a few more places, not sure why it=E2=80=99s only highlighed in =
the place the code was copied to, not the place it was copied from.</div><d=
iv><br></div><div>I think TS-14 is spurious, this should not be called twic=
e, the first caller nulls out the pointer after the cleanup.=C2=A0 The one =
corner case where it can be called twice is if cleanup *reallocates* the TL=
S, in which case doing the cleanup twice is correct.=C2=A0 Do you have a te=
st case that demonstrates this?</div><div><br></div><div>RB-6 looks like th=
e right fix, simple copy-and-paste bug.=C2=A0 Note that this happens only w=
hen memory is exhausted, at which point most Objective-C programs will star=
t failing.</div><div><br></div><div>RB-7, the null check is in a silly plac=
e (after the dereference), but the API contract here is that the argument m=
ust not be null, so it=E2=80=99s actually dead code.=C2=A0 This function al=
so should be setting `*outCount =3D 0` in the early returns.</div><div><br>=
</div><div>PF-6, yes that refactoring would probably be good to do, though =
note that we don=E2=80=99t hit the weak lock in most cases, only if an obje=
ct is marked as having weak refs.=C2=A0 Have you measured slowdown from thi=
s on anything that *isn=E2=80=99t* a contrived microbenchmark?=C2=A0 The qu=
oted slowdown looks incredibly unlikely unless you have a microbenchmark do=
ing nothing but hitting weak references from multiple threads.</div><div><b=
r></div><div>PF-7, this will generate exactly the same code unless we expli=
citly use a weaker memory order (both are sequentially consistent by defaul=
t).=C2=A0 We should move this code over to C++11 atomics at some point.</di=
v><div><br></div><div>TS-3 is incorrect.=C2=A0 This counter grows monotonic=
ally.=C2=A0 If a selector is registered *while* this call is happening, the=
n the result is undefined.=C2=A0 It=E2=80=99s technically UB, in that there=
 is an unsynchronised read.</div><div><br></div><div>PF-4 was an intentiona=
l design choice.=C2=A0 Method replacements are infrequent.=C2=A0 The propos=
ed change would make things worse.</div><div><br></div><div>David</div><div=
><div><br><blockquote type=3D"cite"><div>On 13 Apr 2026, at 04:35, Todd Whi=
te &lt;[email protected]&gt; wrote:</div><br><div><div dir=3D"ltr">=
<div><div class=3D"gmail_default" style=3D"font-family:arial,sans-serif"><s=
pan style=3D"font-family:Arial,Helvetica,sans-serif">Hi GNUstep Team,</span=
></div><br><span class=3D"gmail_default" style=3D"font-family:arial,sans-se=
rif">As an exercise to test out the latest Claude AI capabilities, w</span>=
e recently completed a comprehensive, bottom-up code audit of the GNUstep c=
ore stack =E2=80=94 <span class=3D"gmail_default" style=3D"font-family:aria=
l,sans-serif">all=C2=A0</span>seven repositories =E2=80=94 covering libobjc=
2, libs-base, libs-corebase, libs-opal, libs-quartzcore, libs-gui, and libs=
-back. The full results, documentation, and all fix commits are publicly av=
ailable at:<br><br><a href=3D"https://github.com/DTW-Thalion/gnustep-audit"=
 target=3D"_blank">https://github.com/DTW-Thalion/gnustep-audit</a><br><br>=
I wanted to share what we found and offer to contribute any or all of the c=
hanges back upstream.<br><br>## What we did<br><br>We audited the entire st=
ack bottom-up =E2=80=94 runtime through UI layer =E2=80=94 examining every =
file for robustness issues, thread safety gaps, security vulnerabilities, c=
orrectness bugs, and performance bottlenecks. Each finding was severity-rat=
ed, fixed in an atomic commit tagged with a finding ID, and validated with =
a dedicated regression test. We also wrote 13 performance benchmarks with a=
 baseline/compare workflow so improvements can be measured reproducibly.<br=
><br>## What we found and fixed<br><br>Across all seven repos, we identifie=
d and fixed 150 findings:<br><br>- 22 Critical =E2=80=94 including NSSecure=
Coding bypass (class whitelist completely unimplemented), TLS server verifi=
cation disabled by default, use-after-free in objc_exception_rethrow, NULL =
dereferences, data races in CFRunLoop and CATransaction, zero thread safety=
 across the entire libs-back backend (189 files, 0 locks), and a swapped se=
ndto() argument in CFSocket that prevented any data from being sent.<br><br=
>- 46 High =E2=80=94 deadlocks in property spinlocks, race conditions, buff=
er overflows (CGContext dash buffer allocated in bytes instead of doubles),=
 broken APIs, JSON parser with no recursion depth limit (stack overflow DoS=
), and integer overflow in binary plist bounds checking.<br><br>- 61 Medium=
 =E2=80=94 thread safety gaps in GSLayoutManager, NSView, and NSApplication=
 event dispatch; missing input validation; and general robustness issues.<b=
r><br>- 14 Low + 10 confirmed bugs =E2=80=94 documentation issues, minor op=
timizations, swapped arguments, wrong variables, and inverted conditions (e=
.g., TIFF destination init was inverted, making TIFF writing 100% broken).<=
br><br>We also implemented 12 targeted performance optimizations, including=
:<br><br>- 64-way lock striping for weak references (5=E2=80=938=C3=97 conc=
urrent throughput)<br>- O(1) LRU linked list for NSCache (replacing an O(n)=
 implementation that also never evicted)<br>- Geometric growth for CFArray =
(O(n) vs O(n=C2=B2) sequential appends)<br>- X11 expose event coalescing, l=
ive resize throttling at 60fps, dirty region tracking in NSView, DPSimage c=
onversion caching, and stack buffer allocation in CFRunLoop to eliminate pe=
r-iteration malloc<br><br>Benchmark results on MSYS2/ucrt64 show +29=E2=80=
=9331% for retain/release, +12=E2=80=9318% for message dispatch, +46=E2=80=
=9355% for array operations, and +25% for NSCache.<br><br>## How the work i=
s organized<br><br>Each of the seven repos has its own fork under our GitHu=
b org (<a href=3D"https://github.com/DTW-Thalion" target=3D"_blank">https:/=
/github.com/DTW-Thalion</a>) with fix commits on master. The gnustep-audit =
repo itself contains:<br><br>- Per-phase findings reports (docs/phase1 thro=
ugh phase6)<br>- A master audit summary (docs/AUDIT-SUMMARY.md)<br>- 51 reg=
ression tests and 13 benchmarks under instrumentation/<br>- A Makefile-driv=
en test and benchmark harness with baseline/compare support<br><br>All 32 r=
egression tests pass on the patched stack (up from 18/32 on unpatched).<br>=
<br>## Offer to contribute upstream<br><br>We&#39;d be happy to contribute =
any or all of these changes back into the main GNUstep repositories =E2=80=
=94 whether as pull requests, individual patches, or in whatever form works=
 best for your workflow.=C2=A0<span class=3D"gmail_default" style=3D"font-f=
amily:arial,sans-serif">Feel free to help yourself to the repo.</span><br><=
br>The security-critical fixes (NSSecureCoding, TLS defaults, JSON depth li=
mit, binary plist overflow) and the confirmed crash bugs (use-after-free, N=
ULL derefs, inverted conditions) are probably the highest-priority candidat=
es for upstream integration<span class=3D"gmail_default" style=3D"font-fami=
ly:arial,sans-serif">.</span><br><br>Please feel free to reach out with any=
 questions. We have a lot of respect for the GNUstep project <span class=3D=
"gmail_default" style=3D"font-family:arial,sans-serif">(and a bit nostalgic=
 for heady days of NeXTStep/OpenStep)=C2=A0</span>and would like to see thi=
s work benefit the broader community.</div><div><br></div><div><div class=
=3D"gmail_default" style=3D"font-family:arial,sans-serif">It&#39;s unclear =
if the codebase is actively maintained or if many people still use it, but =
we hope that this exercise provides some value.</div><br>Best regards,</div=
><div><br></div><div><div class=3D"gmail_default" style=3D"font-family:aria=
l,sans-serif">Todd</div><br clear=3D"all"></div><div><div dir=3D"ltr" class=
=3D"gmail_signature"><div dir=3D"ltr"><b>Todd White</b><div>Managing Direct=
or</div><div><br></div><div></div><div><img src=3D"https://ci3.googleuserco=
ntent.com/mail-sig/AIorK4xw08RqGgDKsHOD8XqEYb6-GgGIJIf1cRrXVWqgRb0R08UESMvM=
dTT1W33NqQlLRUIicyduuegMbbIF" width=3D"96" height=3D"71"><br></div><div>177=
 Huntington Avenue, 17th Floor</div><div>Boston, MA 02115</div><div>Telepho=
ne: +1 617 237-2835 Ext. 101</div><div>EIN: 33-2228263</div><div><br></div>=
<div><a href=3D"https://www.thalion.global/" target=3D"_blank">Website</a>=
=C2=A0| <a href=3D"https://x.com/TTIScience" target=3D"_blank">Twitter/X</a=
>=C2=A0| <a href=3D"https://www.youtube.com/@TTIScience" target=3D"_blank">=
YouTube</a></div><div><br></div><div><a href=3D"https://app.candid.org/prof=
ile/16308928/the-thalion-initiative-us-inc/?pkId=3D39bf89e9-f544-478c-a3fe-=
e157e345181d&amp;isActive=3Dtrue" target=3D"_blank"><img src=3D"https://cdn=
.candid.org/seals-of-transparency/2026/candid-seal-gold-2026.png" width=3D"=
96" height=3D"96" alt=3D"https://app.candid.org/profile/16308928/the-thalio=
n-initiative-us-inc-33-2228263/?pkId=3D39bf89e9-f544-478c-a3fe-e157e345181d=
"></a><br></div></div></div></div></div>
</div></blockquote></div><br></div></div></blockquote></div>

--00000000000094bc4e064f5946ab--