What's The Deal With Anti-Cheat On Linux?

What's The Deal With Anti-Cheat  On Linux?

Over the last few years there's been leaps and bounds made in Linux gaming, with continuous improvements being made to compatibility layers and GPU drivers. With over 15,000 Windows games reported working on Linux thanks to Proton[1], things are golden right (or should I say platinum, à la Proton's rating system)? Sort of.

There are still some significant hurdles that prevent some of the most popular games being played, one of the more prevalent being anti-cheat technology. With the unveiling of the Steam Deck[2], Valve announced it will be working alongside anti-cheat vendors to get ALL Windows games working on Linux in time for the Steam Deck release[3].

Now after confirming that I am indeed not dreaming (right????), I can't help but feel sceptical. But... why? I don't really know much about the intricacies of anti-cheats, compatibility layers and the hurdles to get this nicely integrated in Linux. So let's embark on a journey to see if we can get any closer to gauging the feasibility of Valve's goals.

  1. https://www.protondb.com
  2. https://www.steamdeck.com/en/
  3. https://youtu.be/5Q_C5KVJbUw?t=134

Compatibility Layer?

Wine Is Not An Emulator AKA WINE is the OG Windows compatibility layer for Linux. To grossly over simplify, WINE allows Linux users to run Windows programs by translating Windows API (WinAPI) calls to POSIX (Linux compatible API) calls on-the-fly[1].

Proton is Valve's fork of WINE[2] focusing on improving this compatibility layer to provide seamless support specifically for Windows games on Linux, as opposed to WINE's more general but config heavier scope. Proton is integrated into Steam directly via Steam Play, allowing you to run games via Proton at the click of a button.

While I'll just refer to Proton for the rest of the article, this is for brevity and cos gaming; note that it is a fork of WINE, for which some of the work mentioned below is derived.

  1. https://www.winehq.org
  2. https://github.com/ValveSoftware/Proton

Anti-Cheats?

Much like the Autobots and Decepticons, there has been an age-old battle between anti-cheat providers and cheaters. A constant back and forth between bypasses and mitigations, both are constantly evolving and there are different approaches taken by these anti-cheat vendors to tackle cheaters.

It would be out of the scope for this post to detail the different methods employed by anti-cheats but suffice it to say that over the years, the number of mitigations put in place to try and thwart cheaters has stacked up, with ever increasing complexity.

Anti-cheats in many aspects are analogous to anti-virus software, except instead of looking for viruses across a system, they're looking for cheats in a particular game (whether checking game files on disk or the memory used by the game's processes).  

This can involve proactive measures such as simple file integrity checks to see if the game files have been tampered with; detecting malicious code being injected into process memory; implementing anti-debugging measures to hamper reverse engineering, all the way to behavioural measures that make use of algorithms, machine learning and other buzzwords to ingest game data and try and determine cheaters similar to how a human could intuitively spot repeated 720° cross-map no-scopes with no need to reload and go that's probably not legit[1].  

  1. https://superjumpmagazine.com/the-anti-cheat-arms-race-c19343be1dd

So What's The Problem?

So we've touched lightly on how Proton lets us Linux plebs play Windows games and the role anti-cheats play in gaming; where's the problem?

Too Much Integrity (and DRM)

Earlier we mentioned that Proton works by translating the Windows API calls the program is expecting into calls that the Linux kernel understands. It does this by providing its own version of the user-space WinAPI and libraries (DLLs).

Syscalls

Without diving too much into Computer Science 101, the user-space (lower privileged) applications sometimes need to interact with the kernel (higher privileged) to carry out tasks or get information. Syscalls (system calls) are programmatic, clearly defined ways for a user program to interact with the kernel. E.g. from the man page for open:

The open() system call opens the file specified by pathname.  If the specified file does not exist, it may optionally (if O_CREAT is specified in flags) be created by open().[1]
The return value of open() is a file descriptor, a small, nonnegative integer that is an index to an entry in the process's table of open file descriptors.  The file descriptor is used in subsequent system calls (read(2), write(2), lseek(2), fcntl(2), etc.) to refer to the open file. [1]

While Windows and Linux both use syscalls, they implement the calls differently.

Proton's Workaround

By providing its own version of the WinAPI and library DLLs, Proton can implement the same functionality and interface the program expects, but manage the OS interaction using Linux friendly syscalls.

The issue arises when, and is an increasing trend within Windows games, the Windows program calls these syscalls directly, rather than using the WinAPI or libraries. Because Proton only provides the WinAPI and DLLs, those syscalls go directly from the Windows program to the Linux kernel. Yikes!

Last year discussion started making ground on introducing a workaround for this issue into the Linux kernel, with patches introduced by Collabora's Gabriel Krisman Bertazi[2]. Due to the anti-debugging and integrity checking measures we mentioned earlier, it isn't as straight forward as patching the Windows process's syscalls after we load it into memory.

As mentioned in the title to this section, similar but-not-quite-the-same anti-tamper/DRM features (think more anti-piracy than anti-cheat) which are sometimes bundled in by anti-cheat vendors also have a similar impact and were a driving factor in resolving this issue.

At the time, the main option was to use the ptrace() system call to trap on every syscall a usermode application made, at which point Proton could then handle the redirect. Understandably this required a considerable performance hit.

The ptrace() system call provides a means by which one process (the "tracer") may observe and control the execution of another process (the "tracee"), and examine and change the tracee's memory and registers.  It is primarily used to implement breakpoint debugging and system call tracing.[3]

An initial patch involved using seccomp, a Linux kernel feature called secure computing mode. Seccomp also provided a similar ability to intercept syscalls but had the same drawbacks regarding scope. The tl;dr of the patch set was to add some changes to user mode memory mapping and seccomp to allow usermode processes to specify a specific range (i.e just the Windows program being run).

There was some good discussion regarding using a security feature for non-security related use-cases, minimising additions to the kernel and more. After some rework and several iterations later a final patch set was submitted[4], ultimately resulting in...

Omg it's SYSCALL_USER_DISPATCH!

That's right, after many a Youtube video and Reddit post speculating about the impact of SYSCALL_USER_DISPATCH, it actually happened. But what is it exactly?

The feature is the evolution of work led by Collabora, working in cooperation with Valve, and finally made it into mainline with the 5.11 kernel in February 2021. Syscall User Dispatch (SUD) allows Proton to catch and redirect syscalls for specific regions of a process (e.g. our Windows program), with relative ease.

So really, what's the problem?

Did I just write an entire section on a problem that has actually been solved? Possibly. Maybe I was interested in finding more about it and got carried away, but in my defence it is a reasonably new feature and I'm not entirely sure if it's integrated into Proton yet, please let me know!

Looking at their repo[5], I can see the feature PROTON_USE_SECCOMP to emulate native syscalls is obsolete since 5.13 (released pre SUD in 2020 though) and changelogs[6] make no mention of SUD or even seccomp.  

  1. https://man7.org/linux/man-pages/man2/open.2.html
  2. https://lwn.net/ml/linux-kernel/20200530055953.817666-1-krisman@collabora.com/
  3. https://man7.org/linux/man-pages/man2/ptrace.2.html
  4. https://lwn.nt/ml/linux-kernel/20201127193238.821364-1-krisman@collabora.com/
  5. https://github.com/ValveSoftware/Proton
  6. https://github.com/ValveSoftware/Proton/wiki/Changelog#513-1

They Did WHAT (Kernel Level Anti-Cheats)?!

So, remember earlier we mentioned how kernel maintainers are kind of picky about what gets put in the kernel? Well, cheaters have no such qualms and will happily load up their own kernel drivers; basically writing their own code and loading it into the kernel, allowing it to run with all the elevated privileges that entails.

That means that a user-space anti-cheat has absolutely no vision or control over this kernel driver, making it's job suddenly a lot harder if the cheater is meddling with the game on the kernel side (e.g. altering values returned by syscalls made by the game's process).

The response from some anti-cheat vendors on was to follow the cheaters into the kernel, in order to leverage those privileges and increase their efficacy, especially against cheaters also running code in the kernel. Despite the controversy, this wasn't something entirely new, as their analogous counterparts from earlier, anti-viruses, have also employed the same technique to be able to ensure the integrity of the kernel itself, tracking system resources and more. The options are suddenly a lot broader.

There's been plenty of good discussion on the decision for some of these vendors to move their anti-cheats to the kernel centring around privacy and security concerns. However I'll not rehash that here as it's out of scope for this post.

That's Bad Right?

Yes, forget your privacy and security concerns, we've spent this entire time talking about how our amazing Proton & WINE developers provide a translation layer between the Windows program running in user-space and the Linux kernel... AND NOW ANTI-CHEATS ARE STICKING STUFF RIGHT IN THE KERNEL, BYPASSING ALL THAT WORK?! Damn it.

The issue here is the anti-cheat client / Windows game will be making calls and checks to a kernel driver that doesn't exist. As we've discussed, Proton provides a matching user-land API of Windows but doesn't actually implement the Windows kernel, so there's nowhere for the anti-cheat kernel driver to actually run.

Plus, before you say it, remember earlier we mentioned that the way Windows & Linux go about handling syscalls is different? Well that goes for the entire kernel. Unfortunately (or let's be real, fortunately), shoving the Windows anti-cheat kernel drivers into Linux won't work.

It's worth highlighting this is specifically when trying to run Windows games that rely on kernel-level anti-cheat on Linux. Several big anti-cheat providers do have Linux builds for their anti-cheats, but of course these need a native Linux build of the game too.

What to do?

So finally we've reached one of Valve's hurdles to getting anti-cheats to play nice that hasn't already been solved (as far as I'm aware). So, quick recap on where we're at:

  • We currently have a compatibility layer to interface between Windows games running in user-space and the Linux kernel
  • We need a way to run Windows games which rely on interacting with Windows kernel components (anti-cheat drivers)
  • We want an approach with minimal footprint on the Linux kernel, especially given the timeframe (though this doesn't rule out their own Linux kernel drivers)
  • We know Valve are working with at least two anti-cheat vendors (EAC & BattleEye) on a solution

So what are the options here? I'm finally going to have to put my thinking cap on...

Honestly I'm still not entirely sure about how they're going to go about this, and I still don't know nearly enough about Windows internals or anti-cheat implementation to give a definitive answer, but I'll give it a good shot. This is a semi-educated guess from me after a weekends research and my existing Linux experience. Who knows, maybe come December and the Steam Deck release I'll be able to find out how wrong I was!

I think we can rule out any kind of Linux kernel driver implementation. Besides the same security and privacy concerns, and general dislike for proprietary drivers in the community (except you of course, my beautiful NVIDIA drivers), there are some other concerns.

Namely maintaining a proprietary Linux driver is no walk in the park. The Linux API is far from static, and there would be a large range of versions to maintain and constantly update. There's also the caveat that driver development != user-mode development and that there can be serious consequences to mistakes, from crashes to data loss. I'm not ruling out a generic driver to assist Proton/WINE but a per-vendor anti-cheat solution.

Again, I'm not entirely sure how much these anti-cheat technologies vary, but they're strictly proprietary and I imagine One Driver To Rule Them All isn't feasible.

WINE internals[1]

So that brings me on to our diagram up above (yes, maybe I should have included this earlier, sorry!). I imagine a Windows application will communicate with an anti-cheat driver via IOCTLs[2].

It does this by calling DeviceIoControl in Kernel32.dll, which in turn calls NtDeviceIoControlFile in ntdll.dll and at this point things are passed off to the kernel and thus driver via a syscall, which is out of scope for WINE.

My initial guess would be that perhaps anti-cheat vendors and/or Valve would provide a WINE compatible "anti-cheat driver" that would sit under NTDLL in the diagram below. Much like WINE provides it's own version of Windows dlls, this would be a similar affair. The "anti-cheat driver" would use the same IOCTL-based API, such that the Win32 application would communicate with it via the same route as it would on native Windows.

This way we avoid the pain of running and maintaining a Linux kernel driver and in fact avoid having to run anything in the kernel at all. That said, how feasible is it to implement Windows kernel anti-cheat functionality in this pseudo-Windows environment? I'm not aware of any of the moving parts here but it seems like a large time investment for vendors to develop these WINE "drivers", as none of the Windows kernel interface or functionality is there via WINE, so I imagine quite a lot would need to be reworked.

tl;dr I fee like they'll avoid the Linux kernel for anti-cheat implementations, but the alternative of squeezing into WINE seems almost as difficult.

EDIT: Of course I found this AFTER publishing. Shoutout to @Guy15241 & @0xdt0 who pioneered some early, unofficial work on getting EAC working with WINE prior to all this.[3] There's an update post from a year ago with some details about it here.[4]

Their approach sounds somewhat similar, integrating EAC driver into the WINE environment and getting IOCTL interaction to play nicely, but there certainly looks to be like some more intricacies with the driver-game interaction, such as when injecting into the Windows program.

  1. https://www.researchgate.net/figure/Windows-NT-architecture-WINE-Architecture_fig6_221926549
  2. https://docs.microsoft.com/en-gb/windows/win32/devio/device-input-and-output-control-ioctl-
  3. https://www.gamingonlinux.com/2020/06/wine-so-proton-eventually-takes-another-step-towards-easy-anti-cheat-working
  4. https://www.reddit.com/user/Guy1524/comments/hc3o1y/eac_progress_update/

Conclusion

I had no idea what I was getting into when I started this post, if it wasn't obvious, there was no overarching plan. I started with the question of how Valve intends to tackle the prevalent issues of Windows anti-cheats & Proton, so that these games are playable via the Steam Deck. My gut reaction was "surely not by December" but I didn't know why.

There are certainly other pain points for getting Windows games on Linux that need to be solved, touched on in a great presentation by Collabora here[1]. However, I wanted to narrow the scope a little and touch on the anti-cheat issues specifically.

Hopefully if you've made it this far you've taken something, somewhere from this little adventure into asking some of the questions about the issues involved in this topic and forgive me some of my "educated" guesses towards the end.

I struggled to come up with too much information regarding how Valve plans to solve the kernel anti-cheat issue, so I had a look at some diagrams, pulled from the weekends research and grasped at some straws. If you have any solid leads or ideas on this I'd be keen to hear them!

I can't say I'm much closer to being able to say whether or not Valve's goals are feasible, but I sure hope they release an equally as interesting blog post on how they went about it!

  1. https://static.sched.com/hosted_files/osseu2020/ea/Gabriel_Krisman_Bertazi-linux_gaming_in_2020-kernel.pdf

exit(0);

Show Comments