[PATCH] Cygwin: gendef: fix AArch64 _sigfe_maybe control flow
Aswin Kalies Ramkumar Mangayarkarasi
aswin.kalies@multicorewareinc.com
Mon Sep 21 12:04:58 GMT 2026
Hi Jon,
This is a fix for the _sigfe_maybe stub you reviewed in
https://cygwin.com/pipermail/cygwin-patches/2026q3/015305.html
You wrote:
>> Hmmm... while staring at this code, trying to refresh my memory
>> of how this it all works... this seems wrong.
>>
>> Comparing this to the x86_64 version of sigfe_maybe, shouldn't
>> the branch here be to forward to label 1? Otherwise this
>> function effectively does nothing.
You are right, thank you for catching it. The patch adds a
.L_sigfe_entry label inside the AArch64 _sigfe at the position
matching x86_64's label 1:, and branches to it with b.eq.
Fixing that turned up two more defects in the same stub.
The bypass path could not work either. On x86_64 the generated
wrapper pushes the address of the real function, so 'ret' pops it and
jumps there. AArch64 'ret' branches through x30, and the generated
wrapper is
sub sp, sp, 16
str x9, [sp, 0] // x9 = &real_function
br x9 // br, not bl: x30 still holds the original
// caller's return address
so 'ret' returned straight to that caller without calling the real
function, leaving sp 16 bytes low on every call. It is replaced with
'ldr x9, [sp], #16' and 'br x9', which is what _sigfe itself does on
exit.
The magic value was also built with its halves reversed: movz #0xc763
/ movk #0x173f assembles 0x173fc763, not CYGTLS_INITIALIZED
(0xc763173f). The first defect masked this, since the comparison
result was discarded.
>> (Of course, this isn't very important since sigfe_maybe is only
>> used by cygwin_detach_dll())
Agreed, and that is why none of it was noticed.
Validation: the generated sigfe.s and the linked aarch64 cygwin1.dll
were both checked to confirm the branch and the constant came out as
intended. At runtime on a Windows ARM64 host, a test program
dlopen()/dlclose()d a Cygwin DLL 10000 times against the patched
cygwin1.dll; all cycles completed without fault, and a marker byte
written by the test DLL's destructor was counted 10000 times,
confirming each dlclose really reached the detach path.
Two limitations: the loop was not run against a pre-fix build, so it
has not been shown to fail on the original code; and it cannot tell
which of the two _sigfe_maybe paths is taken, since a thread that
wrongly took the bypass would still call cygwin_detach_dll and still
return 0 from dlclose, losing only the signal-frame bookkeeping.
Thanks and Regards,
Aswin Kalies
Inline patch
---
winsup/cygwin/scripts/gendef | 31 +++++++++++++++++++------------
1 file changed, 19 insertions(+), 12 deletions(-)
diff --git a/winsup/cygwin/scripts/gendef b/winsup/cygwin/scripts/gendef
index 5f25c543e..ddbe50d58 100755
--- a/winsup/cygwin/scripts/gendef
+++ b/winsup/cygwin/scripts/gendef
@@ -368,25 +368,32 @@ EOF
.seh_proc _sigfe_maybe
_sigfe_maybe: # stack is aligned on entry!
.seh_endprologue
- ldr x10, [x18, #0x8] // Load TEB pointer in x10
- ldr x11, =_cygtls.initialized // Load relative offset of _cygtls.initialized
- add x11, x10, x11 // compute absolute address and store in x11
- cmp sp, x11 // Compare current stack pointer with TLS location
- b.hs 0f // if sp >= tls, skip TLS logic
- ldr w12, [x11] // Load the value at _cygtls.initialized (32-bit)
- movz w13, #0xc763 // Prepare magic value(0xc763173f) lower 16 bits
- movk w13, #0x173f, lsl #16 // Add upper 16 bits, full value now in w13
- cmp w12, w13 // Compare loaded value with magic
- b.ne 0f // If not equal, not initialized, skip TLS logic
- ret
+ // x18 is the Windows TEB pointer. Its field at offset 8 is the base
+ // used to resolve offsets within Cygwin's _cygtls block.
+ ldr x10, [x18, #0x8] // Load the Cygwin TLS base into x10
+ ldr x11, =_cygtls.initialized // Load the _cygtls.initialized offset
+ add x11, x10, x11 // Compute &_cygtls.initialized
+ cmp sp, x11 // Compare SP with the TLS location
+ b.hs 0f // No usable Cygwin TLS if SP >= it
+ ldr w12, [x11] // Load the initialization marker
+ movz w13, #0x173f // Prepare magic value 0xc763173f, low half
+ movk w13, #0xc763, lsl #16 // Add high half: 0xc763173f
+ cmp w12, w13 // Compare the marker with the expected magic value
+ b.eq .L_sigfe_entry // Valid TLS: use the normal signal front end
0:
- ret
+ // The wrapper reserved 16 bytes and stored the real function at [sp].
+ // AArch64 ret uses x30 and does not pop that wrapper slot.
+ ldr x9, [sp], #16 // Remove the wrapper slot and load the target
+ br x9 // Enter the target while preserving x30
.seh_endproc
.seh_proc _sigfe
_sigfe:
.seh_endprologue
ldr x10, [x18, #0x8] // Load TLS base into x10
+.L_sigfe_entry:
+ // _sigfe_maybe reaches this point after loading x10; direct _sigfe calls
+ // reach it through the TLS-base load above.
mov w9, #1 // constant value for lock acquisition
0: ldr x11, =_cygtls.stacklock // Load offset of stacklock
add x12, x10, x11 // Compute final address of stacklock
--
2.49.0.windows.1
-------------- next part --------------
A non-text attachment was scrubbed...
Name: Cygwin-gendef-fix-AArch64-_sigfe_maybe-control-flow.patch
Type: application/octet-stream
Size: 3813 bytes
Desc: Cygwin-gendef-fix-AArch64-_sigfe_maybe-control-flow.patch
URL: <https://cygwin.com/pipermail/cygwin-patches/attachments/20260921/80f18b8f/attachment.obj>
More information about the Cygwin-patches
mailing list