What did RunSybil find in CPython?
Security firm RunSybil published research on 3 November 2026 describing a memory-safety flaw in CPython, the reference implementation of Python. The company says the bug lets an attacker escape a Jinja code sandbox and run arbitrary code inside the process that serves an AI model.
The flaw sits in CPython's handling of generic types, the machinery behind expressions such as dict[str, int]. CPython caches the parameters of a generic alias in a tuple called __parameters__. Any object with a __typing_subst__ method can occupy a slot, even when it is not a real TypeVar.
RunSybil's exploit changes the rules partway through substitution. A callback adds __typing_subst__ to an argument after the cache is built. CPython's lookup then returns -1. A defensive assertion would catch that, but release builds compile assertions out. The interpreter reads argitems[-1] and hands whatever it finds to the attacker's callback.
That read normally crashes Python. RunSybil arranged for the out-of-bounds slot to hold a valid, useful object pointer instead. From CPython 3.14 onward, a tuple's cached hash sits immediately before its element pointers. Hashing a tuple of integers is deterministic, so the researchers could choose integers whose hash equalled a target address.
That primitive recovered the live str class, then the type and code classes. With those constructors, the template could build a code object and call it. RunSybil says the escape works from an empty sandbox context, because the objects it needs are exposed to every template.
The researchers say they found the exploit in less than a day, combining human work with GPT-5.6 Sol. They present that speed as part of the problem. A memory-safety bug that once needed weeks of manual work can now be turned into a working exploit quickly.
How can a model's chat template run code?
A chat template is the part of a model download that tells an inference server how to turn a conversation into a prompt. Hugging Face's Transformers documentation describes chat templates as part of the tokenizer configuration. Servers such as vLLM render the template on each request.
The template is also the one part of a model package that is plain code. Weight files are data, and tokenizer files describe vocabulary. A Jinja template contains expressions, filters and loops, and the server evaluates them every time a user sends a message.
Jinja ships a sandbox for exactly this job. Its SandboxedEnvironment and ImmutableSandboxedEnvironment block private attributes, unsafe calls and mutation. The documentation says the sandbox can render untrusted templates, while warning that the sandbox alone is not a complete security solution.
RunSybil took Qwen2.5-0.5B-Instruct, created a modified copy with a backdoored chat_template.jinja file, and loaded it in vLLM. When the server rendered a prompt, the template executed code and wrote a file containing the output of the id command to /tmp. The code ran in the serving process, not in a separate sandbox process.
The proof of concept did not need a poisoned weight file. The payload lived in a text template stored beside the model, so a review that checked only the binary weights would have found nothing unusual. That makes the issue a review problem as much as a memory-safety problem.
"Breaking out of the chat template sandbox and executing code represents a supply chain risk," RunSybil researcher Darius Houle wrote. The post argues that the risk reaches inference providers, library developers and model users, not just the person who uploaded a model.
The scenario that matters most is bring-your-own-model hosting. A provider that accepts customer checkpoints may render a tenant's template in a shared serving process. RunSybil shared proof-of-concept exploits with several such providers and reported a wide range of responses.
The demonstration is a proof of concept, not evidence of exploitation in the wild. It requires an attacker to get a malicious model or template in front of a vulnerable server. It also requires a service that renders that template with a vulnerable CPython build and without stronger isolation.
Who is responsible for fixing a sandbox escape?
RunSybil reported the bug to CPython, the Pallets Project, which maintains Jinja, and Hugging Face. It gave them a 90-day disclosure window. What followed was a dispute about ownership rather than a dispute about the bug.
CPython's security policy states that "CPython does not support sandboxing untrusted Python code as a security boundary". It adds that escapes from such a sandbox should be reported to the sandbox's developers instead.
The Python Security Response Team repeated that position to CSO Online. It said "we do not consider the demonstrated sandbox escape to be a CPython security vulnerability". The team also said RunSybil reported the issue by email rather than through CPython's designated private GitHub channel.
Jinja's maintainers pointed the report back the other way. RunSybil quotes their reply: "Report this to cpython. It's problem with cpython, not with Jinja." The Pallets project also asked the researchers to read its policy on AI-assisted contributions before continuing.
Hugging Face did not respond during the 90-day window, according to RunSybil. vLLM acknowledged the report but had not decided whether to ship a mitigation. Both companies are central to the exploit chain: one distributes model artifacts, the other renders their templates.
RunSybil published on 3 November, after the 90-day window it had set. The firm says it followed the response team's guidance by submitting a public patch, then explained the escape once coordination had run its course. CPython's reply to CSO disputes the process, not the technical fix.
The impasse reached a wider audience on 5 November, when Oracle's Alan Coopersmith forwarded the research to the oss-security mailing list. Gentoo developer Sam James replied with a link to LWN's 2013 article on the failure of pysandbox. That project's author had declared it a failure and warned others away from that style of sandbox.
The history explains the stalemate. Python's maintainers have long argued that a language runtime cannot police code it was never designed to confine. Sandbox authors have long depended on the runtime's memory safety anyway. AI inference added a new place where those assumptions meet.
Is there a patch, and which Python versions have it?
A fix exists. RunSybil's Darius Houle opened pull request 155761, titled "Do not crash when GenericAlias parameters change during substitution", on 13 August 2026. CPython merged it to the main branch the next day. The change touches three files, adds 30 lines and removes two.
The patch checks the lookup result before indexing. When an argument gains a substitution hook after the cache was built, CPython now raises TypeError instead of reading memory before the argument array. That turns a memory-safety bug into an error path.
The public issue, 155752, frames the problem as a crash rather than a security flaw. It describes how a release build uses the -1 as an array index. The pull request adds a regression test in Lib/test/test_typing.py and a changelog entry, alongside 12 added lines in Objects/genericaliasobject.c.
Backports followed. The 3.15 pull request merged on 14 August. The 3.14 and 3.13 backports merged on 8 September. CPython treated all of them as regular bug fixes rather than security releases, because it does not classify the escape as a vulnerability.
There is no 3.12 backport. That matters because Python 3.12 remains in security-only support until October 2028, according to the Python Developer's Guide. Ubuntu 24.04 LTS, a common server base, ships python3 3.12.3. RunSybil says large parts of the AI ecosystem are still aligned on 3.12.
RunSybil warns that security-fix-only builds of CPython are unsafe for workloads that render sandboxed code. The reasoning is simple. A branch that accepts only security fixes will not take this patch if the project does not call it a security fix.
The current release line is 3.15, first released on 9 October 2026. Python 3.14 arrived in October 2025 and stays supported until October 2030. The 3.13 branch receives security fixes until October 2029. Operators on those branches get the change through normal updates, provided they install them.
Who is affected by the CPython sandbox escape?
The short answer is any Python service that renders untrusted Jinja templates, evaluates untrusted expressions, or loads model artifacts that carry either. The bug is in CPython, but the exposure depends on what code the service agrees to run.
Three patterns stand out. Multi-tenant inference providers that accept customer model uploads are the clearest case, especially when templates render inside a shared serving process. Platforms that let users write report, email or workflow templates carry the same shape of risk. Model registries that scan files but treat templates as data also fall short.
Providers with strong isolation limit the blast radius. RunSybil singles out DigitalOcean, which wrapped its bring-your-own-model service in multi-tier isolation. The researchers say the attack was fully contained and they found no way to move laterally. AWS implemented a mitigation in Bedrock after receiving the report.
The blast radius inside one process is larger than it looks. A serving process typically holds API keys, model caches, tenant prompts and network credentials. RunSybil's demonstration wrote a file to /tmp; the same primitive could read environment variables or reach internal services that the process can see.
Not every Python user is exposed. A laptop running a fixed script, a batch job with reviewed templates, or a service that loads only vetted models does not hand an attacker the template path. The risk concentrates where untrusted code, or something that can become code, meets a shared runtime.
The distribution layer adds a second problem. Upstream has no 3.12 patch, so long-term-support distributions have nothing to pull from the branch. They can write their own backport, but no such patch has been announced in the reporting around this bug.
That leaves a gap between the fix and the installed base. Inference images often pin an older Python for compatibility with compiled libraries. Rebuilding those images on 3.13 or newer is a project, not a version bump.
How should teams reduce the risk?
Start by treating the sandbox as one layer, not the boundary. Jinja's own documentation says the sandbox alone is not a solution for perfect security. RunSybil's recommendation goes further: treat template engines and expression evaluators as best-effort controls.
The practical controls are familiar from container security. None of them depends on the CPython patch, and each one shrinks what a successful escape can reach.
Version upgrades help only where a fixed branch is available. Moving a serving stack to 3.13, 3.14 or 3.15 picks up the change. Staying on 3.12 means relying on isolation and input control until the branch reaches end of life.
Detection is limited but worth building. The proof of concept wrote a file to /tmp, which a hardened container would block or log. A process that renders chat templates should not spawn shells, open network connections or write outside its working directory. Those events are useful signals.
The same logic applies beyond AI. Any Python application that evaluates user-supplied expressions inherits this class of risk. Auditing where templates come from is cheaper than auditing every escape technique.
- •Render templates in a separate process with no network access, a read-only filesystem and a minimal seccomp profile.
- •Give each tenant's model its own container or microVM, so one escape does not reach another customer.
- •Review chat_template.jinja and tokenizer_config.json before loading a model, and restrict uploads to reviewed artifacts.
- •Run serving containers as non-root with CPU and memory limits, and log writes outside the working directory.
- •Track the Python version inside images, not just on the host, and rebuild when a patched branch lands.
What to watch next
The first question is vLLM. The project acknowledged the report but had not committed to a mitigation. A server-side template policy, such as refusing unreviewed templates or rendering them out of process, would change the risk for every deployment that uses it.
The second is Hugging Face. RunSybil says the company did not answer within the disclosure window. Model hubs could add template review, signing or warnings, but none of that has been announced.
The third is the 3.12 gap. Ubuntu 24.04 LTS stays supported until 2029, while upstream Python 3.12 leaves security support in October 2028. Without a backport, a large installed base runs without the fix for the rest of the decade.
The fourth is the research pipeline. RunSybil found this exploit in under a day with an AI model and human review, and says it is publishing a series on CPython memory safety. More findings in the same area are likely, and each one raises the same ownership question.
The firm frames this work as the second entry in that series. Its earlier research used a documented CPython out-of-bounds read to escape a sandbox. The new post says the flaw behind this exploit had not been reported before, which suggests the bug class is far from exhausted.
The bug is fixed in current Python. The disagreement about who owns sandbox escapes is not. Until that changes, the practical answer sits with operators: assume a template can run code, and build the layers that keep that code contained.
