Skip to content

Commit 01b661f

Browse files
leaanthonyclaude
andauthored
feat(v2): add runtime.ResetSignalHandlers() for Linux panic recovery (#4921)
* feat(v2): add runtime.ResetSignalHandlers() for Linux panic recovery Add a new runtime function that allows users to reset signal handlers before code that might panic from nil pointer dereferences. On Linux, WebKit installs signal handlers without the SA_ONSTACK flag, which prevents Go from properly recovering from panics caused by SIGSEGV and other signals. This function adds SA_ONSTACK to the relevant signal handlers (SIGSEGV, SIGBUS, SIGFPE, SIGABRT). Usage: ```go go func() { defer func() { if err := recover(); err != nil { log.Printf("Recovered: %v", err) } }() runtime.ResetSignalHandlers() // Code that might panic... }() ``` The function is a no-op on macOS and Windows. Fixes #3965 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(v2): add panic-recovery-test example Add an example that demonstrates the Linux signal handler issue (#3965) and verifies the fix using runtime.ResetSignalHandlers(). The example includes: - A Greet function that triggers a nil pointer dereference after a delay - Auto-call from frontend after 5 seconds - README with reproduction steps Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 896344e commit 01b661f

File tree

22 files changed

+1133
-0
lines changed

22 files changed

+1133
-0
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Panic Recovery Test
2+
3+
This example demonstrates the Linux signal handler issue (#3965) and verifies the fix using `runtime.ResetSignalHandlers()`.
4+
5+
## The Problem
6+
7+
On Linux, WebKit installs signal handlers without the `SA_ONSTACK` flag, which prevents Go from recovering panics caused by nil pointer dereferences (SIGSEGV). Without the fix, the application crashes with:
8+
9+
```
10+
signal 11 received but handler not on signal stack
11+
fatal error: non-Go code set up signal handler without SA_ONSTACK flag
12+
```
13+
14+
## The Solution
15+
16+
Call `runtime.ResetSignalHandlers()` immediately before code that might panic:
17+
18+
```go
19+
import "github.com/wailsapp/wails/v2/pkg/runtime"
20+
21+
go func() {
22+
defer func() {
23+
if err := recover(); err != nil {
24+
log.Printf("Recovered: %v", err)
25+
}
26+
}()
27+
runtime.ResetSignalHandlers()
28+
// Code that might panic...
29+
}()
30+
```
31+
32+
## How to Reproduce
33+
34+
### Prerequisites
35+
36+
- Linux with WebKit2GTK 4.1 installed
37+
- Go 1.21+
38+
- Wails CLI
39+
40+
### Steps
41+
42+
1. Build the example:
43+
```bash
44+
cd v2/examples/panic-recovery-test
45+
wails build -tags webkit2_41
46+
```
47+
48+
2. Run the application:
49+
```bash
50+
./build/bin/panic-recovery-test
51+
```
52+
53+
3. Wait ~10 seconds (the app auto-calls `Greet` after 5s, then waits another 5s before the nil pointer dereference)
54+
55+
### Expected Result (with fix)
56+
57+
The panic is recovered and you see:
58+
```
59+
------------------------------"invalid memory address or nil pointer dereference"
60+
```
61+
62+
The application continues running.
63+
64+
### Without the fix
65+
66+
Comment out the `runtime.ResetSignalHandlers()` call in `app.go` and rebuild. The application will crash with a fatal signal 11 error.
67+
68+
## Files
69+
70+
- `app.go` - Contains the `Greet` function that demonstrates panic recovery
71+
- `frontend/src/main.js` - Auto-calls `Greet` after 5 seconds to trigger the test
72+
73+
## Related
74+
75+
- Issue: https://github.com/wailsapp/wails/issues/3965
76+
- Original fix PR: https://github.com/wailsapp/wails/pull/2152
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"time"
7+
8+
"github.com/wailsapp/wails/v2/pkg/runtime"
9+
)
10+
11+
// App struct
12+
type App struct {
13+
ctx context.Context
14+
}
15+
16+
// NewApp creates a new App application struct
17+
func NewApp() *App {
18+
return &App{}
19+
}
20+
21+
// startup is called when the app starts. The context is saved
22+
// so we can call the runtime methods
23+
func (a *App) startup(ctx context.Context) {
24+
a.ctx = ctx
25+
}
26+
27+
// Greet returns a greeting for the given name
28+
func (a *App) Greet(name string) string {
29+
go func() {
30+
defer func() {
31+
if err := recover(); err != nil {
32+
fmt.Printf("------------------------------%#v\n", err)
33+
}
34+
}()
35+
time.Sleep(5 * time.Second)
36+
// Fix signal handlers right before potential panic using the Wails runtime
37+
runtime.ResetSignalHandlers()
38+
// Nil pointer dereference - causes SIGSEGV
39+
var t *time.Time
40+
fmt.Println(t.Unix())
41+
}()
42+
43+
return fmt.Sprintf("Hello %s, It's show time!", name)
44+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8"/>
5+
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
6+
<title>panic-test</title>
7+
</head>
8+
<body>
9+
<div id="app"></div>
10+
<script src="./src/main.js" type="module"></script>
11+
</body>
12+
</html>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"name": "frontend",
3+
"private": true,
4+
"version": "0.0.0",
5+
"scripts": {
6+
"dev": "vite",
7+
"build": "vite build",
8+
"preview": "vite preview"
9+
},
10+
"devDependencies": {
11+
"vite": "^3.0.7"
12+
}
13+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#logo {
2+
display: block;
3+
width: 50%;
4+
height: 50%;
5+
margin: auto;
6+
padding: 10% 0 0;
7+
background-position: center;
8+
background-repeat: no-repeat;
9+
background-size: 100% 100%;
10+
background-origin: content-box;
11+
}
12+
13+
.result {
14+
height: 20px;
15+
line-height: 20px;
16+
margin: 1.5rem auto;
17+
}
18+
19+
.input-box .btn {
20+
width: 60px;
21+
height: 30px;
22+
line-height: 30px;
23+
border-radius: 3px;
24+
border: none;
25+
margin: 0 0 0 20px;
26+
padding: 0 8px;
27+
cursor: pointer;
28+
}
29+
30+
.input-box .btn:hover {
31+
background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%);
32+
color: #333333;
33+
}
34+
35+
.input-box .input {
36+
border: none;
37+
border-radius: 3px;
38+
outline: none;
39+
height: 30px;
40+
line-height: 30px;
41+
padding: 0 10px;
42+
background-color: rgba(240, 240, 240, 1);
43+
-webkit-font-smoothing: antialiased;
44+
}
45+
46+
.input-box .input:hover {
47+
border: none;
48+
background-color: rgba(255, 255, 255, 1);
49+
}
50+
51+
.input-box .input:focus {
52+
border: none;
53+
background-color: rgba(255, 255, 255, 1);
54+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
Copyright 2016 The Nunito Project Authors (contact@sansoxygen.com),
2+
3+
This Font Software is licensed under the SIL Open Font License, Version 1.1.
4+
This license is copied below, and is also available with a FAQ at:
5+
http://scripts.sil.org/OFL
6+
7+
8+
-----------------------------------------------------------
9+
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
10+
-----------------------------------------------------------
11+
12+
PREAMBLE
13+
The goals of the Open Font License (OFL) are to stimulate worldwide
14+
development of collaborative font projects, to support the font creation
15+
efforts of academic and linguistic communities, and to provide a free and
16+
open framework in which fonts may be shared and improved in partnership
17+
with others.
18+
19+
The OFL allows the licensed fonts to be used, studied, modified and
20+
redistributed freely as long as they are not sold by themselves. The
21+
fonts, including any derivative works, can be bundled, embedded,
22+
redistributed and/or sold with any software provided that any reserved
23+
names are not used by derivative works. The fonts and derivatives,
24+
however, cannot be released under any other type of license. The
25+
requirement for fonts to remain under this license does not apply
26+
to any document created using the fonts or their derivatives.
27+
28+
DEFINITIONS
29+
"Font Software" refers to the set of files released by the Copyright
30+
Holder(s) under this license and clearly marked as such. This may
31+
include source files, build scripts and documentation.
32+
33+
"Reserved Font Name" refers to any names specified as such after the
34+
copyright statement(s).
35+
36+
"Original Version" refers to the collection of Font Software components as
37+
distributed by the Copyright Holder(s).
38+
39+
"Modified Version" refers to any derivative made by adding to, deleting,
40+
or substituting -- in part or in whole -- any of the components of the
41+
Original Version, by changing formats or by porting the Font Software to a
42+
new environment.
43+
44+
"Author" refers to any designer, engineer, programmer, technical
45+
writer or other person who contributed to the Font Software.
46+
47+
PERMISSION & CONDITIONS
48+
Permission is hereby granted, free of charge, to any person obtaining
49+
a copy of the Font Software, to use, study, copy, merge, embed, modify,
50+
redistribute, and sell modified and unmodified copies of the Font
51+
Software, subject to the following conditions:
52+
53+
1) Neither the Font Software nor any of its individual components,
54+
in Original or Modified Versions, may be sold by itself.
55+
56+
2) Original or Modified Versions of the Font Software may be bundled,
57+
redistributed and/or sold with any software, provided that each copy
58+
contains the above copyright notice and this license. These can be
59+
included either as stand-alone text files, human-readable headers or
60+
in the appropriate machine-readable metadata fields within text or
61+
binary files as long as those fields can be easily viewed by the user.
62+
63+
3) No Modified Version of the Font Software may use the Reserved Font
64+
Name(s) unless explicit written permission is granted by the corresponding
65+
Copyright Holder. This restriction only applies to the primary font name as
66+
presented to the users.
67+
68+
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
69+
Software shall not be used to promote, endorse or advertise any
70+
Modified Version, except to acknowledge the contribution(s) of the
71+
Copyright Holder(s) and the Author(s) or with their explicit written
72+
permission.
73+
74+
5) The Font Software, modified or unmodified, in part or in whole,
75+
must be distributed entirely under this license, and must not be
76+
distributed under any other license. The requirement for fonts to
77+
remain under this license does not apply to any document created
78+
using the Font Software.
79+
80+
TERMINATION
81+
This license becomes null and void if any of the above conditions are
82+
not met.
83+
84+
DISCLAIMER
85+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
87+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
88+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
89+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
91+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
92+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
93+
OTHER DEALINGS IN THE FONT SOFTWARE.
Binary file not shown.
136 KB
Loading
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import './style.css';
2+
import './app.css';
3+
4+
import logo from './assets/images/logo-universal.png';
5+
import {Greet} from '../wailsjs/go/main/App';
6+
7+
document.querySelector('#app').innerHTML = `
8+
<img id="logo" class="logo">
9+
<div class="result" id="result">Please enter your name below 👇</div>
10+
<div class="input-box" id="input">
11+
<input class="input" id="name" type="text" autocomplete="off" />
12+
<button class="btn" onclick="greet()">Greet</button>
13+
</div>
14+
</div>
15+
`;
16+
document.getElementById('logo').src = logo;
17+
18+
let nameElement = document.getElementById("name");
19+
nameElement.focus();
20+
let resultElement = document.getElementById("result");
21+
22+
// Setup the greet function
23+
window.greet = function () {
24+
// Get name
25+
let name = nameElement.value;
26+
27+
// Check if the input is empty
28+
if (name === "") return;
29+
30+
// Call App.Greet(name)
31+
try {
32+
Greet(name)
33+
.then((result) => {
34+
// Update result with data back from App.Greet()
35+
resultElement.innerText = result;
36+
})
37+
.catch((err) => {
38+
console.error(err);
39+
});
40+
} catch (err) {
41+
console.error(err);
42+
}
43+
};
44+
45+
// Auto-call Greet after 5 seconds to trigger the panic test
46+
setTimeout(() => {
47+
console.log("Auto-calling Greet to trigger panic test...");
48+
Greet("PanicTest")
49+
.then((result) => {
50+
resultElement.innerText = result + " (auto-called - panic will occur in 5s)";
51+
})
52+
.catch((err) => {
53+
console.error("Error:", err);
54+
});
55+
}, 5000);
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
html {
2+
background-color: rgba(27, 38, 54, 1);
3+
text-align: center;
4+
color: white;
5+
}
6+
7+
body {
8+
margin: 0;
9+
color: white;
10+
font-family: "Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
11+
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
12+
sans-serif;
13+
}
14+
15+
@font-face {
16+
font-family: "Nunito";
17+
font-style: normal;
18+
font-weight: 400;
19+
src: local(""),
20+
url("assets/fonts/nunito-v16-latin-regular.woff2") format("woff2");
21+
}
22+
23+
#app {
24+
height: 100vh;
25+
text-align: center;
26+
}

0 commit comments

Comments
 (0)