Guide · Backend
GitLab's October 2026 Regex Vulnerability Explained: Patch GitLab and Ruby
19 min readUpdated By Saket Bhatnagar
Researched and drafted with AI, reviewed and edited by Saket Bhatnagar.
In short
- The GitLab regex vulnerability, CVE-2026-110264, lets an attacker who is not logged in run code on a self-managed GitLab server. A regex (regular expression) is a text pattern such as a.*b.
- The bug sits in Onigmo, the regex engine inside Ruby. Building a crafted pattern can corrupt memory before any text is matched, and GitLab compiles patterns its users type.
- Fix GitLab by upgrading to the fixed release for your series, such as 19.5.1 or 19.4.3, released on 21 October 2026. Ruby apps that compile user-typed patterns need Ruby 4.0.9, 3.4.12 or 3.3.13.
- Regexp.timeout does not protect you, says the Ruby team, and a length limit is not reliable either. A timeout watches matching, and these bugs fire while the pattern is being compiled.
- Beginners can stop after the section on checking your own Ruby apps. Engineers then get the overflow mechanics from the public fixes, safer ways to accept patterns, and why Ruby has not filed its own CVEs.
What goes wrong when a server compiles a regex its users typed?
The server can be taken over by the pattern alone. In the GitLab regex vulnerability of October 2026, a specially crafted regex corrupts memory while Ruby turns it into a program, before it ever searches any text.
Picture a common setup. A software team in Pune runs its own GitLab server for 60 developers, and also builds a Rails support tool where agents type regex filters for tickets. The setup is made up, but both risks are real.
The team now has two jobs. Upgrade GitLab, which ships its own patched Ruby, and check whether the support tool compiles patterns that customers or agents type. This guide walks through both on that one setup.
Which words do you need before reading about this GitLab bug?
- Regex (regular expression): a small pattern language for matching text. The pattern ^ERR\d+ matches lines that start with ERR and then digits. A | in a pattern means or, as in cat|dog.
- Compiling a regex: turning the pattern text into a small program of instructions that the engine runs later. In Ruby, Regexp.new does this.
- Onigmo: the regex engine built into Ruby. Every Ruby regex, including those inside GitLab, is compiled and run by Onigmo.
- CVE: a public ID number for one security bug, such as CVE-2026-110264. CVSS is a 0 to 10 severity score; this one scores 10.0.
- Self-managed GitLab: a GitLab server you install and run yourself, as the Pune team does, instead of using GitLab's hosted service.
What is the GitLab regex vulnerability CVE-2026-110264?
The GitLab regex vulnerability is a critical bug where compiling a specially crafted regular expression overflows an integer and can let an unauthenticated user run code on the GitLab server. GitLab rates it CVSS 10.0.
An integer overflow happens when a number grows past the largest value its storage can hold and wraps around, like an odometer rolling back to zero. The engineer section below shows it running.
GitLab fixed it in a critical patch release on 21 October 2026. Every GitLab version from 7.0 up to the fixed releases is affected, so almost any self-managed server that has not upgraded since then is exposed.
It is the last of four related CVEs. GitLab fixed CVE-2026-89078 and CVE-2026-93577 in 19.4.1 and CVE-2026-97229 in 19.4.2, all bugs in how Ruby builds patterns, according to the Ruby team's advisory.
| GitLab CVE | Bug in Ruby's regex engine | Ruby change | First fixed in Ruby 4.0 / 3.4 / 3.3 |
|---|---|---|---|
| CVE-2026-89078 (GitLab 19.4.1) | Double free when parsing a pattern fails after an alternation | Pull request 13332 | 4.0.0 / 3.4.12 / 3.3.13 |
| CVE-2026-93577 (19.4.1) and CVE-2026-97229 (19.4.2) | Integer overflow in the size of a compiled pattern | Pull request 19020 | 4.0.8 / 3.4.12 / 3.3.13 |
| CVE-2026-110264 (19.5.1) | Overflow of internal IDs in a pattern with many repeats | Pull request 16801 | 4.0.9 / 3.4.12 / 3.3.13 |
The 4.0.0 entry means Ruby 4.0 already had that one fix when it first shipped, so on Ruby 4.0 the other two fixes arrived in 4.0.8 and 4.0.9.
Who is affected by the October 2026 Ruby regex bugs?
You are affected if your software compiles a regex from text an attacker controls, and your Ruby is older than 4.0.9, 3.4.12 or 3.3.13. Apps that only use patterns written by their own developers are not affected, says the Ruby advisory.
The Ruby team names four ways in. Three are methods: Regexp.new, Regexp.compile and Regexp.union. The fourth is dropping text into a pattern written in the code, as in /#{input}/.
GitLab, the Ruby team writes, uses Regexp.new on patterns its users write, such as those in CI configuration, the file that tells GitLab how to build and test code.
| You run | Are you exposed? | What to do |
|---|---|---|
| GitLab.com or GitLab Dedicated | No. GitLab says its own infrastructure is already patched | Nothing for this bug |
| Self-managed GitLab, any version from 7.0 before the fixed releases | Yes. GitLab rates CVE-2026-110264 critical, CVSS 10.0 | Upgrade GitLab now |
| A Ruby app that compiles user-typed patterns | Yes, if Ruby is older than 4.0.9, 3.4.12 or 3.3.13 | Upgrade Ruby and stop compiling raw user text where you can |
| A Ruby app that only uses developer-written patterns | No, per the Ruby advisory | Upgrade Ruby in your normal cycle |
| Ruby 3.2 or older | The advisory lists no fix for that branch | Move to Ruby 3.3.13, 3.4.12 or 4.0.9 |
Patching decision for the Pune team
Drag to exploreNotice that GitLab and the team's own app are patched separately. Upgrading one does not fix the other.
Diagram connections:
- Start: list what runs Ruby to Self-managed GitLab? (Check GitLab first)
- Self-managed GitLab? to Upgrade GitLab (Yes)
- Self-managed GitLab? to App compiles user text? (No)
- Upgrade GitLab to App compiles user text? (Then your apps)
- App compiles user text? to Upgrade Ruby now (Yes)
- App compiles user text? to Upgrade in normal cycle (No)
- Upgrade Ruby now to Verify versions (Check)
- Upgrade in normal cycle to Verify versions (Check)
- Start: list what runs Ruby: GitLab server and the support tool
- Self-managed GitLab?: GitLab.com and Dedicated are already patched
- Upgrade GitLab: To 19.5.1, 19.4.3, 19.3.6 or a backport
- App compiles user text?: grep for Regexp.new, union, match
- Upgrade Ruby now: 4.0.9, 3.4.12 or 3.3.13
- Upgrade in normal cycle: Developer-written patterns only
- Verify versions: gitlab:env:info and ruby -v
How do you patch a self-managed GitLab server?
Upgrade GitLab to the fixed release in your series. The Ruby advisory says GitLab fixed these issues by patching the Ruby it ships, so a Linux package install gets the fix with the upgrade.
| Your GitLab series | Upgrade to at least |
|---|---|
| 19.5 | 19.5.1 |
| 19.4 | 19.4.3 |
| 19.3 | 19.3.6 |
| 19.2 | 19.2.9 (backport) |
| 19.1 | 19.1.9 (backport) |
| 19.0 | 19.0.10 (backport) |
| 18.11 | 18.11.13 (backport) |
Find your GitLab version
On a Linux package install, run sudo gitlab-rake gitlab:env:info on a node that runs GitLab Rails. Read the Version line under GitLab information.
Pick the target release
Match your series in the table above. If you run an older series than 18.11, you need a version upgrade to a supported series, not only a patch.
Plan for migrations
GitLab says 19.4.3 and 19.3.6 include database migrations, so single-node instances on those series will have downtime. Multi-node setups can avoid downtime with GitLab's zero-downtime upgrade steps. Take a backup first.
Upgrade and restart
Install the new package the way you normally upgrade. On a multi-node setup, follow GitLab's documented upgrade order.
Check that it worked
Run gitlab:env:info again. The Version line should show the fixed release or later, for example 19.5.1-ee on Enterprise Edition.
# Linux package (Omnibus) install
sudo gitlab-rake gitlab:env:info
# Self-compiled install, from the GitLab directory
bundle exec rake gitlab:env:info RAILS_ENV=productionA self-compiled install is different. GitLab's install docs tell you to install Ruby yourself from source, and the patch notes give no separate steps for it. Upgrade that Ruby too, to the fixed release on a branch your GitLab version supports, and check the Ruby Version line.
GitLab's install docs still list Ruby 3.2.x as the minimum, as of 24 October 2026. The Ruby advisory lists no fix for 3.2, so a source install sitting on 3.2 needs a newer Ruby.
GitLab also lists two detection names for self-managed instances: Onigmo native worker crash and Org mode render error burst. GitLab released these detections for self-managed instances, and they are listed on the patch page. Treat a sudden burst of either as a reason to look closer, but patch first.
How do you check your own Ruby apps?
First find where your code compiles patterns, then decide whether the text can come from a user. A plain text search over the code base is a good start, not a full audit.
grep -rnE 'Regexp(\.|::)(new|compile|union)|/#\{|%r\{[^}]*#\{|\.match\??\(' app lib config 2>/dev/nullWe ran this on a small sample file from the support tool. It found four lines: a Regexp.new on a user's rule, an escaped interpolation, a Regexp.union of strings and a raw interpolation of user text. The 2>/dev/null part hides errors for folders you do not have.
The search also flags .match and .match? calls. In our tests on Ruby 3.3.6, a string passed to match or match? was compiled as a pattern, so "axb".match?("a.b") returned true. The advisory does not list these, but they compile text too.
Then check the Ruby version that actually runs the app, inside the container or on the server. Run ruby -v there; you need 4.0.9, 3.4.12 or 3.3.13 or later on your branch.
How does compiling a regex turn into code execution?
Compiling is a separate step from matching, and all three bugs live in that first step, in the parser and the compiler. Onigmo reads the pattern, builds a tree of its parts, then writes a program of instructions into memory.
If a size or ID count wraps around during that build, the engine can write or jump to the wrong place in memory. GitLab says this could lead to arbitrary code execution. Corrupted memory is a common route attackers use to take control of a process.
Where the October 2026 bugs sit in a regex's life
Drag to exploreNotice that the bugs fire in the parser and compiler boxes, before any text is matched. That is why a matching timeout cannot catch them.
Diagram connections:
- User types a pattern to Ruby app code (Pattern text)
- Ruby app code to Onigmo parser (Regexp.new)
- Onigmo parser to Onigmo compiler (Pattern tree)
- Onigmo compiler to Compiled program (Program and sizes)
- Compiled program to Matcher (Used on each search)
- User types a pattern: For example a filter in a form or config file
- Ruby app code: Calls Regexp.new with the text
- Onigmo parser: Builds a tree; the double free started here
- Onigmo compiler: Writes the program; the overflows were here
- Compiled program: Kept in memory and reused for matches
- Matcher: Runs the program on text; timeouts apply here
Integer overflow in plain numbers
Think of a car's odometer with six digits. After 999999 km it shows 000000, and anyone reading it thinks the car is new. A computer integer does the same at its own limit.
A signed 32-bit integer tops out at 2,147,483,647, which engineers call INT_MAX. Add one and it becomes a large negative number. You can see this safely in Ruby:
max = 2**31 - 1
puts "INT_MAX = #{max}"
wrapped = [max + 1].pack("l").unpack1("l") # store the value in a signed 32-bit slot
puts "INT_MAX + 1 in 32 bits = #{wrapped}"On Ruby 3.3.6 this prints INT_MAX = 2147483647, then INT_MAX + 1 in 32 bits = -2147483648. Ruby's own integers grow without limit, so the pack call forces the value into a 32-bit slot, like the fixed-size numbers inside Onigmo's C code.
Where the picture breaks: an odometer only misleads a buyer. Inside a compiler, a wrapped number can decide how much memory to set aside or where to jump, so the program can write past the space it planned.
What the public fixes show
Pull request 19020, merged on 25 September 2026, explains the size overflow in its description. A nested bounded repeat multiplies two counts as an int, the product wraps, and a safety check passes for a repeat far larger than allowed.
The same description says long chains of repeats can push the compiled program past INT_MAX, which wraps the jump offsets stored inside it. The fix compares by division, which cannot overflow, and caps the program size as it is written.
Pull request 16801, the fix behind CVE-2026-110264, is titled Reject overflowed repeat/null-check IDs in huge regexps. It adds explicit errors when a pattern needs too many internal IDs for repeats, instead of letting the counter wrap.
Pull request 13332 fixes a double free, where a program hands the same block of memory back twice. Its description says the old code could free the same parse node twice when parsing and compiling a pattern hit an error.
Its title mentions too big repetition quantifiers, while the advisory describes the trigger as a parse failure after an alternation. Both point at error paths in the same code.
How can you accept user search text more safely?
Most features do not need user-written regexes at all. If users want to find a word or phrase, match it as plain text, and the special characters lose their meaning.
input = "price (in Rs.)?"
safe = Regexp.new(Regexp.escape(input))
puts safe.source # price\ \(in\ Rs\.\)\?
puts safe.match?("total price (in Rs.)? 500") # true
puts "total price (in Rs.)? 500".include?(input) # true, no regex at all
puts Regexp.union("a.b", "c|d").source # a\.b|c\|dWe ran these lines on Ruby 3.3.6 and got the outputs in the comments. Regexp.escape puts a backslash before each special character, and Regexp.union escapes plain strings the same way.
An escaped pattern has no repeats or alternation, the parts these three fixes deal with. The Ruby advisory does not discuss escaped input, so upgrade Ruby anyway. Simplest of all, String#include? avoids the regex engine entirely.
Watch for Regexp.union given Regexp objects: those are used as they are, not escaped. And /#{text}/ with raw text compiles that text as a pattern, so a user can type any pattern into it.
If users really must write patterns
Some features need real patterns, such as log filters or CI rules. The Ruby team's own advice, if you cannot upgrade, is to avoid compiling regexes from untrusted input at all.
- Upgrade Ruby first. That is the only fix the Ruby team recommends for these bugs.
- Limit who can create patterns. A pattern field open to anyone on the internet is a bigger risk than one only admins can edit.
- Compile untrusted patterns in a separate, short-lived worker process with few privileges. A crash or takeover then hits a box with nothing valuable in it.
- Log compile errors and crashes. A RegexpError is normal for a typo, but a crash in the regex engine is not.
Why does Regexp.timeout not protect against these bugs?
Regexp.timeout limits how long matching may run, and these bugs happen while compiling. The Ruby advisory states plainly that Regexp.timeout does not protect against them, and that length limits do not reliably help either.
Pause and predict: the code below sets Regexp.timeout to 0.05 seconds. It then compiles a long, harmless 2.2 MB pattern and matches a tiny, slow one. Which of the two does Ruby stop?
Regexp.timeout = 0.05
pat = "(?:abc|abd)" * 200_000 # long but ordinary
t = Process.clock_gettime(Process::CLOCK_MONOTONIC)
re = Regexp.new(pat) # compile: the timeout is not checked here
puts Process.clock_gettime(Process::CLOCK_MONOTONIC) - t
slow = Regexp.new('^(a|a)*\1$') # backreference, so the match cache is off
begin
slow.match?("a" * 40 + "b")
rescue Regexp::TimeoutError => e
puts e.class # Regexp::TimeoutError
endOn Ruby 3.3.6, compiling the harmless pattern took 0.24 seconds and raised no error. Matching the slow pattern raised Regexp::TimeoutError after 0.051 seconds. Timings vary by machine.
What just happened? The compile step ran past the time limit and finished anyway. Only the match step checked the clock, so a bug that fires while compiling never meets the timeout.
Length limits fail for a different reason. The advisory says some of these bugs can be triggered by short patterns, so a 200-character cap can still let a dangerous pattern through.
Why has Ruby not filed its own CVEs for these fixes?
The Ruby team says it has generally treated compiling a regex from untrusted input as unsafe, so it is still deciding whether these count as Ruby vulnerabilities. As of 23 October 2026, it has not requested CVE IDs.
The advisory also points to precedent. Ruby did treat a double free in regex compiling as a vulnerability in CVE-2022-28738, after a full assessment, and did the same for CVE-2024-27282.
The advisory also admits a gap in the docs. Ruby's Regexp documentation discusses untrusted patterns only as a denial-of-service risk and recommends a timeout. It does not say whether compiling untrusted patterns is supported.
For now, the Ruby team asks you to track these fixes by GitLab's CVE IDs, for example in a security scanner. A scanner that looks only for Ruby CVEs may miss them, so check your Ruby version by hand too.
Ruby 3.3 is in security maintenance, where releases normally carry only vulnerability fixes. The team made an exception for 3.3.13 because GitLab assigned CVEs to issues that rely on these changes.
What does this teach about accepting user input as code?
A regex is a tiny program, so a user-written regex is user-written code. Any feature that compiles it inherits every bug in the compiler, not just the risk of a slow match.
The same lesson shows up with template engines, query languages and spreadsheet formulas. Give users the smallest language that does the job, run the risky part with few privileges, and keep the runtime patched.
At least two of the bugs came through GitLab's bug bounty. The Ruby advisory credits Johan Carlsson for the overflow fixed by pull request 19020, and GitLab's patch notes thank the same researcher, joaxcar, for CVE-2026-110264.
How do you explain the GitLab regex vulnerability in a system design interview?
Name the trust boundary
Say that any feature letting users submit patterns treats user text as a small program. Mark the compile step as untrusted work.
Separate the two risks
Slow matching (ReDoS) is handled with timeouts. Memory bugs during compiling need patched runtimes and isolation, as GitLab's October 2026 fix showed.
Prefer a smaller feature
Offer plain-text search or a fixed set of filters first. Allow full patterns only where the product truly needs them.
Isolate what remains
Compile and run user patterns in a separate worker with few privileges and strict resource limits, so a crash cannot reach secrets.
Plan patching and detection
Track runtime versions across the fleet, alert on regex engine crashes, and rehearse a same-day upgrade for critical fixes.
Questions people ask
- Is GitLab.com affected by CVE-2026-110264?
- No action is needed on GitLab.com. GitLab's patch notes say GitLab.com is already running the patched version, and GitLab Dedicated tenants on 19.3.5-internal0 or 19.4.2-internal0 or later already include the fixes. If you run GitLab yourself, upgrade to 19.5.1, 19.4.3, 19.3.6 or the backport for your series.
- Do I need to upgrade Ruby separately on a GitLab server?
- Not on a Linux package install. The Ruby advisory says GitLab fixed these issues by patching the Ruby it ships, so upgrading GitLab is enough. A self-compiled install runs on a Ruby you installed, so upgrade that Ruby too. Other Ruby apps on the machine need their own upgrade.
- Will Regexp.timeout or a length limit protect my Rails app?
- No, neither is a fix for these bugs. The Ruby advisory says Regexp.timeout does not protect against them and length limits are not reliable, because some can be triggered by short patterns. Upgrade to Ruby 4.0.9, 3.4.12 or 3.3.13, and avoid compiling untrusted patterns where you can.
- Is Regexp.escape on user input safe?
- It is much safer than compiling raw user text, because escaping turns the input into a plain literal with no repeats or alternation. The Ruby advisory does not discuss escaped input directly, so still upgrade Ruby. For simple phrase search, String#include? avoids the regex engine completely.
- What should I do if I am still on Ruby 3.2?
- Move to a supported branch. Ruby 3.2 reached end of life on 1 April 2026, and the advisory lists fixes only for 4.0, 3.4 and 3.3. Ruby 3.3 itself is in security maintenance, with end of life expected on 31 March 2027, so 3.4 or 4.0 gives you longer support.
- Is there public exploit code for the GitLab regex vulnerability?
- As of 24 October 2026, GitLab has not published exploit details, and it makes issue details public 90 days after a fix. The Ruby advisory describes the bugs without an attack walkthrough. The fix pull requests include short example patterns, which you do not need to patch.
- How is this different from ReDoS?
- ReDoS is a denial of service where a match takes very long and ties up a worker. These bugs corrupt memory while a pattern is being compiled, which GitLab says could allow code execution. Timeouts help with ReDoS; only patched Ruby or GitLab fixes these.
Sources and further reading
- GitLab: Critical Patch Release 19.5.1, 19.4.3, 19.3.6, 19.2.9, 19.1.9, 19.0.10, 18.11.13 (21 Oct 2026)
- Ruby: Regexp compilation fixes related to GitLab's CVEs (23 Oct 2026)
- Ruby pull request 19020: Avoid overflow in the regexp compiler's program size arithmetic
- Ruby pull request 16801: Reject overflowed repeat/null-check IDs in huge regexps
- Ruby pull request 13332: Prevent double free for too big repetition quantifiers
- Ruby: release list with dates for 4.0.9, 3.4.12 and 3.3.13
- Ruby: maintenance branches and end-of-life dates
- GitLab docs: maintenance Rake tasks (gitlab:env:info)
- GitLab docs: self-compiled installation (Ruby section and software requirements)