Files
hytale-f2p-mirror/docs/STEAMDECK_CRASH_INVESTIGATION.md
sanasol dc664afa52 docs: Update crash investigation - no stable solution found
- jemalloc helps ~30% of the time but not reliable
- Documented all failed approaches (allocators, scheduling, patching variations)
- Added potential alternative approaches (network hooking, proxy, container)
- Status: UNSOLVED

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 06:28:09 +01:00

6.4 KiB

Steam Deck / Ubuntu LTS Crash Investigation

Status: UNSOLVED

Last updated: 2026-01-27

No stable solution found. jemalloc helps occasionally but crashes still occur randomly.


Problem Summary

The Hytale F2P launcher's client patcher causes crashes on Steam Deck and Ubuntu LTS with the error:

free(): invalid pointer

or

SIGSEGV (Segmentation fault)

The crash occurs after successful authentication, specifically right after "Finished handling RequiredAssets".

Affected Systems:

  • Steam Deck (glibc 2.41)
  • Ubuntu LTS

Working Systems:

  • macOS
  • Windows
  • Older Arch Linux (glibc < 2.41)

Critical Finding: The UNPATCHED original binary works fine on Steam Deck. The crash is caused by ANY binary patching.


What Was Tried (All Failed)

Memory Allocators

Approach Result
LD_PRELOAD=/usr/lib/libjemalloc.so.2 Works randomly (3/10 times), not stable
MALLOC_CHECK_=0 No effect
MALLOC_PERTURB_=255 No effect
GLIBC_TUNABLES=glibc.malloc.tcache_count=0 No effect

Process/Scheduling

Approach Result
taskset -c 0 (single core) Game too slow, stuck at connecting
taskset -c 0,1 or 0-3 Still crashes
nice -n 19 No effect
chrt --idle 0 No effect
strace -f No effect

Linker/Loading

Approach Result
LD_BIND_NOW=1 No effect
Wrapper script with LD_PRELOAD No effect
Shell spawn with inline LD_PRELOAD No effect

Patching Variations

Approach Result
Null-padding after replacement Crashes (made it worse)
No null-padding (develop behavior) Still crashes
Minimal patches (3 instead of 6) Still crashes
Ultra-minimal (1 patch - domain only) Still crashes
Skip sentry patch Still crashes
Skip subdomain patches Still crashes

Key Finding: Even patching just 1 string (main domain only) causes the crash.


String Occurrences Found

Length-Prefixed Format

Found by default patcher mode:

Offset Content Notes
0x1bc5d63 hytale.com Surrounded by x86 code!

UTF-16LE Format (3 occurrences)

Offset Content
0x1bc5ad7 sentry.hytale.com/...
0x1bc5b3f https://hytale.com/help...
0x1bc5bc9 store.hytale.com/?...

Binary Analysis

When patching with length-prefixed mode:

< 01bc5d60: 5933 b80a 0000 0068 0079 0074 0061 006c  Y3.....h.y.t.a.l
< 01bc5d70: 0065 002e 0063 006f 006d 8933 8807 0000  .e...c.o.m.3....
---
> 01bc5d60: 5933 b80a 0000 0073 0061 006e 0061 0073  Y3.....s.a.n.a.s
> 01bc5d70: 006f 006c 002e 0077 0073 8933 8807 0000  .o.l...w.s.3....

Structure:

5933 b8 | 0a000000 | h.y.t.a.l.e...c.o.m | 8933 8807 0000
???????? | len=10   | string content      | mov [rbx],esi?
  • 5933 b8 before string - could be code or metadata
  • 0a 00 00 00 - .NET length prefix (10 characters)
  • String content in UTF-16LE
  • 89 33 after - this is mov [rbx], esi in x86-64!

The string is embedded near executable code, not in a clean data section.


GDB Stack Trace

#0  0x00007ffff7d3f5a4 in ?? () from /usr/lib/libc.so.6
#1  raise () from /usr/lib/libc.so.6
#2  abort () from /usr/lib/libc.so.6
#3-#4  ?? () from /usr/lib/libc.so.6
#5  free () from /usr/lib/libc.so.6
#6  ?? () from libzstd.so    <-- CRASH POINT
#7-#24  HytaleClient code (asset decompression)

Crash occurs in libzstd.so during free() after "Finished handling RequiredAssets".


Hypotheses

1. .NET AOT String Metadata (Most Likely)

.NET AOT may have precomputed hashes, checksums, or relocation info for strings. Modifying string content breaks internal consistency, causing memory corruption when the runtime tries to use related data structures.

2. Code/Data Interleaving

The strings are embedded near x86 code (89 33 = mov [rbx], esi). .NET AOT may use relative offsets that get invalidated when we modify nearby bytes.

3. Binary Checksums

The binary may have integrity checks for certain sections that we're invalidating by patching.

4. Timing-Dependent Race Condition

The fact that it works randomly (~30% of the time with jemalloc) suggests a race condition that's affected by:

  • Memory layout changes from patching
  • Allocator behavior differences
  • CPU scheduling

Valgrind Results (Misleading)

  • Valgrind showed NO memory corruption errors
  • Game ran successfully under Valgrind (slower execution)
  • This suggested jemalloc would fix it, but it doesn't consistently work

The slowdown from Valgrind likely masks the race condition timing.


Current Launcher Implementation

The launcher attempts:

  1. Auto-detect jemalloc at common paths
  2. Auto-install jemalloc via pkexec if not found
  3. Launch game with LD_PRELOAD via shell command

But this doesn't provide stable results.


Potential Alternative Approaches (Not Yet Tried)

1. LD_PRELOAD Network Hooking

Instead of patching the binary, hook getaddrinfo() / connect() to redirect network calls at runtime. No binary modification needed.

2. Local Proxy + Certificate

Run a local HTTPS proxy that intercepts hytale.com traffic and redirects to custom server. Requires installing a custom CA certificate.

3. DNS + iptables Redirect

Use local DNS to resolve hytale.com to localhost, then iptables to redirect to actual custom server. Requires root/sudo.

4. Container with Older glibc

Run the game in a container with glibc < 2.41 where the stricter validation doesn't exist.

5. Different Patching Location

Find strings in a pure data section rather than code-adjacent areas.


Files Reference

Binary: HytaleClient (ELF 64-bit, ~39.9 MB)

Branch: fix/steamdeck-jemalloc-crash


Install jemalloc (Partial Mitigation)

jemalloc may help in some cases (~30% success rate):

# Steam Deck / Arch Linux
sudo pacman -S jemalloc

# Ubuntu / Debian
sudo apt install libjemalloc2

# Fedora / RHEL
sudo dnf install jemalloc

The launcher automatically uses jemalloc if found. To disable:

HYTALE_NO_JEMALLOC=1 npm start

Conclusion

No stable solution found. The binary patching approach may be fundamentally incompatible with glibc 2.41's stricter heap validation when modifying .NET AOT compiled binaries.

Alternative approaches (network hooking, proxy, container) may be required for reliable Steam Deck / Ubuntu LTS support.