Skip to content

Commit 326e61e

Browse files
myleshortonclaude
andcommitted
Wrap GetSplitTunnelItems and GetEnabledApps with RunOffCgoStack
Both functions allocate pointer-bearing Go types ([]string, map) on the CGo callback stack, causing bulkBarrierPreWrite panics on macOS when the GC tries to install write barriers on non-heap memory. This is the same bug fixed for GetAvailableServers in #8541 and GetAutoLocation in radiance#361. The fix is identical: run the function body on a real Go goroutine via RunOffCgoStack. Fixes getlantern/engineering#3088 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent fe09a5d commit 326e61e

1 file changed

Lines changed: 86 additions & 82 deletions

File tree

lantern-core/core.go

Lines changed: 86 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -943,40 +943,42 @@ func (lc *LanternCore) splitTunnelHandler() (*vpn.SplitTunnel, error) {
943943
}
944944

945945
func (lc *LanternCore) GetSplitTunnelItems(filterType string) (string, error) {
946-
st, err := lc.splitTunnelHandler()
947-
if err != nil {
948-
return "", err
949-
}
946+
return common.RunOffCgoStack(func() (string, error) {
947+
st, err := lc.splitTunnelHandler()
948+
if err != nil {
949+
return "", err
950+
}
950951

951-
f := st.Filters()
952-
953-
var items []string
954-
switch filterType {
955-
case vpn.TypeDomain:
956-
items = f.Domain
957-
case vpn.TypeDomainSuffix:
958-
items = f.DomainSuffix
959-
case vpn.TypeDomainKeyword:
960-
items = f.DomainKeyword
961-
case vpn.TypeDomainRegex:
962-
items = f.DomainRegex
963-
case vpn.TypeProcessName:
964-
items = f.ProcessName
965-
case vpn.TypeProcessPath:
966-
items = f.ProcessPath
967-
case vpn.TypeProcessPathRegex:
968-
items = f.ProcessPathRegex
969-
case vpn.TypePackageName:
970-
items = f.PackageName
971-
default:
972-
return "", fmt.Errorf("unsupported filter type: %s", filterType)
973-
}
952+
f := st.Filters()
953+
954+
var items []string
955+
switch filterType {
956+
case vpn.TypeDomain:
957+
items = f.Domain
958+
case vpn.TypeDomainSuffix:
959+
items = f.DomainSuffix
960+
case vpn.TypeDomainKeyword:
961+
items = f.DomainKeyword
962+
case vpn.TypeDomainRegex:
963+
items = f.DomainRegex
964+
case vpn.TypeProcessName:
965+
items = f.ProcessName
966+
case vpn.TypeProcessPath:
967+
items = f.ProcessPath
968+
case vpn.TypeProcessPathRegex:
969+
items = f.ProcessPathRegex
970+
case vpn.TypePackageName:
971+
items = f.PackageName
972+
default:
973+
return "", fmt.Errorf("unsupported filter type: %s", filterType)
974+
}
974975

975-
b, err := json.Marshal(items)
976-
if err != nil {
977-
return "", err
978-
}
979-
return string(b), nil
976+
b, err := json.Marshal(items)
977+
if err != nil {
978+
return "", err
979+
}
980+
return string(b), nil
981+
})
980982
}
981983

982984
func jsonNumberToIntString(f float64) string {
@@ -1017,67 +1019,69 @@ func (lc *LanternCore) GetAppDataDir() string {
10171019
}
10181020

10191021
func (lc *LanternCore) GetEnabledApps() (string, error) {
1020-
path := filepath.Join(settings.GetString(settings.DataPathKey), "split-tunnel.json")
1021-
b, err := os.ReadFile(path)
1022-
if err != nil {
1023-
if os.IsNotExist(err) {
1022+
return common.RunOffCgoStack(func() (string, error) {
1023+
path := filepath.Join(settings.GetString(settings.DataPathKey), "split-tunnel.json")
1024+
b, err := os.ReadFile(path)
1025+
if err != nil {
1026+
if os.IsNotExist(err) {
1027+
return "[]", nil
1028+
}
1029+
return "", err
1030+
}
1031+
if len(b) == 0 {
10241032
return "[]", nil
10251033
}
1026-
return "", err
1027-
}
1028-
if len(b) == 0 {
1029-
return "[]", nil
1030-
}
10311034

1032-
var m map[string]any
1033-
if err := json.Unmarshal(b, &m); err != nil {
1034-
return "", err
1035-
}
1035+
var m map[string]any
1036+
if err := json.Unmarshal(b, &m); err != nil {
1037+
return "", err
1038+
}
10361039

1037-
candidateKeys := []string{
1038-
"processPathRegex",
1039-
"processPath",
1040-
"packageName",
1041-
"bundleId",
1042-
"bundleID",
1043-
"enabledApps",
1044-
"apps",
1045-
}
1040+
candidateKeys := []string{
1041+
"processPathRegex",
1042+
"processPath",
1043+
"packageName",
1044+
"bundleId",
1045+
"bundleID",
1046+
"enabledApps",
1047+
"apps",
1048+
}
10461049

1047-
seen := map[string]struct{}{}
1048-
out := make([]string, 0, 16)
1050+
seen := map[string]struct{}{}
1051+
out := make([]string, 0, 16)
10491052

1050-
addList := func(v any) {
1051-
arr, ok := v.([]any)
1052-
if !ok {
1053-
return
1054-
}
1055-
for _, it := range arr {
1056-
s, ok := it.(string)
1053+
addList := func(v any) {
1054+
arr, ok := v.([]any)
10571055
if !ok {
1058-
continue
1059-
}
1060-
s = strings.TrimSpace(s)
1061-
if s == "" {
1062-
continue
1056+
return
10631057
}
1064-
if common.IsWindows() {
1065-
s = strings.ToLower(s)
1058+
for _, it := range arr {
1059+
s, ok := it.(string)
1060+
if !ok {
1061+
continue
1062+
}
1063+
s = strings.TrimSpace(s)
1064+
if s == "" {
1065+
continue
1066+
}
1067+
if common.IsWindows() {
1068+
s = strings.ToLower(s)
1069+
}
1070+
if _, exists := seen[s]; exists {
1071+
continue
1072+
}
1073+
seen[s] = struct{}{}
1074+
out = append(out, s)
10661075
}
1067-
if _, exists := seen[s]; exists {
1068-
continue
1069-
}
1070-
seen[s] = struct{}{}
1071-
out = append(out, s)
10721076
}
1073-
}
10741077

1075-
for _, k := range candidateKeys {
1076-
if v, ok := m[k]; ok {
1077-
addList(v)
1078+
for _, k := range candidateKeys {
1079+
if v, ok := m[k]; ok {
1080+
addList(v)
1081+
}
10781082
}
1079-
}
10801083

1081-
encoded, _ := json.Marshal(out)
1082-
return string(encoded), nil
1084+
encoded, _ := json.Marshal(out)
1085+
return string(encoded), nil
1086+
})
10831087
}

0 commit comments

Comments
 (0)