Skip to content

fix(linux): resolve the Mono image base from /proc/maps so Wine/Proton works - #7

Merged
Manwe-777 merged 2 commits into
mtgatool:mainfrom
raymondshiner:fix/linux-wine-mono-base
Aug 8, 2026
Merged

fix(linux): resolve the Mono image base from /proc/maps so Wine/Proton works#7
Manwe-777 merged 2 commits into
mtgatool:mainfrom
raymondshiner:fix/linux-wine-mono-base

Conversation

@raymondshiner

Copy link
Copy Markdown
Contributor

Makes the Linux/Wine Mono path actually reach the runtime, and stops warning users about privileges they already have.

Both bugs surface the same way: Arena installed through Steam on Linux, running under Proton. init() hangs forever, and the desktop app shows "App is not running with admin/elevated privileges" regardless of what the user does.

1. read_mono_root_domain() never returns under Wine

The Linux implementation sweeps the address space from 0 in 4 KiB steps looking for the MZ magic:

let mut addr = 0 as usize;
while !found {
    // read u16 at addr, check for MZ, try the PE header
    addr += 4096;
}

That terminates quickly on Windows, where the image sits within the first few GB. But Wine loads mono-2.0-bdwgc.dll high — on my machine:

6ffffa010000-...  MTGA/MonoBleedingEdge/EmbedRuntime/mono-2.0-bdwgc.dll

0x6ffffa010000 is 123 TB, or 30,064,746,512 iterations, each one a process_vm_readv syscall. The loop is unbounded, so it doesn't fail — it just never finishes.

Since Wine maps the DLL from disk, /proc/<pid>/maps names it outright. That's the same information Process::module() gives the Windows path, so this change simply mirrors what Windows already does: take the base, hand it to PEReader. detection.rs was already reading that exact file to grep for "mono-2.0" — it just discarded the address.

Worth noting the README documents this path as "Windows / Linux (Wine) → Mono", and Wine is the only way Arena runs on Linux, so the scan had no configuration in which it could succeed.

2. is_admin() equates privilege with uid 0

Cross-process memory reads are available to root, to anyone holding CAP_SYS_PTRACE, and to same-uid processes whenever Yama is absent or set to ptrace_scope=0. The last two groups can read Arena's memory fine, but were still told to elevate — advice that wouldn't have helped, since elevation wasn't the missing piece. Now all three conditions are checked.

Verification

Arch Linux, kernel 6.17, Proton Experimental, Arena via Steam (appid 2141910), ordinary user with ptrace_scope=0:

Call Before After
init("MTGA.exe") never returns 2.3 s
isAdmin() false true
readAccount unreachable 8 ms
readRanks unreachable 19 ms
readInventory unreachable 9 ms
readDecks unreachable 400 ms — 95 decks
readCollection unreachable 62 ms

Values cross-checked against the Arena UI: wildcards, gold, gems, vault percentage, and both rank tracks all match. Nothing downstream of the base address needed changing — the Mono walker handles Wine's layout as-is.

cargo check --lib --features napi-bindings is clean; no new warnings.

Notes for review

  • I kept the change to mono_module_base() deliberately narrow. Selecting the mapping at file offset 0 is what puts us on the PE header; the .min() on (file_offset, start) is doing that.
  • I don't have a native-Linux Mono target to regress against, but as far as I can tell one doesn't exist for Arena — if the old scan was serving some other setup, this would need a fallback.
  • Happy to split this into two PRs if you'd prefer the is_admin() change reviewed separately.

The Linux read_mono_root_domain() swept the address space from 0 in 4KiB
steps looking for the MZ magic. Under Wine/Proton — the only way MTGA runs
on Linux, and what the README documents this path as targeting — the game's
mono-2.0-bdwgc.dll is mapped around 0x6ffff_xxxx_xxxx, roughly 3e10 pages
up. The loop is unbounded, so init() never returns.

Wine maps the DLL from disk, so /proc/<pid>/maps names it outright. Take the
base from there and hand it to PEReader, mirroring what the Windows path
already does with Process::module().

Verified against MTGA under Proton Experimental (Arch, kernel 6.17):
init() 2.3s, then readAccount/readRanks/readInventory/readDecks(95)/
readCollection all return correct data matching the Arena UI.
is_admin() equated privilege with uid 0, which is a Windows-shaped
assumption. On Linux, cross-process memory reads are permitted to root, to
any process holding CAP_SYS_PTRACE, and to same-uid processes whenever the
Yama LSM is absent or set to ptrace_scope=0.

Users in those last two groups can read MTGA's memory perfectly well, but
the desktop app still greeted them with "App is not running with
admin/elevated privileges" — a warning with no action behind it, since
elevating was never what they were missing.

Check the actual conditions instead. Verified on Arch (ptrace_scope=0):
isAdmin() now returns true as an ordinary user, and the typed readers
succeed in the same process.
@Manwe-777

Manwe-777 commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Hi @raymondshiner, Thanks you for submitting this PR !

As you noted there is no Mono target specifically for Arena on Linux, if a dedicated version would exist, it would use the IL2CPP implementation instead, thus, we rely on the Mono target on Linux because that is what people use (virtualized windows versiones runnin Mono).

Also, on my tests, the admin requirement on Linux is variable. Usually, it was not required like you mentioned, other times it actually blocked me from reading /proc entirely, even without writing (rust libraries back when I was testing did not discriminate permissions for either and you would be blocked with having to elevate the process just to do reads). this might have changed but I havent been able to test myself in quite some time.

I will check if it causes any regressions against mac/windows, but any more tests are very appreciated! thanks again for this! :)

@Manwe-777
Manwe-777 merged commit 0af144d into mtgatool:main Aug 8, 2026
11 checks passed
@Manwe-777

Copy link
Copy Markdown
Contributor

All looks good, the changes were contained on Linux and honestly without these changes the reader wouldnt even work on Linux as is stands, thanks again !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants