Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Overlay mounts have an undocumented limit on the number of `--overlay-src` layers, and exceeding it reports a misleading error

Open
#804 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

This issue has not been assessed yet.

Description

Summary

bubblewrap passes every overlay layer to mount(2) in a single options string. The kernel keeps at most one page of that string and silently drops the rest, so there is a hard limit on how many --overlay-src layers can be stacked. The limit is not documented, and going over it does not produce an error that points at the layer count. It usually produces Overlay directories may not overlap, which is actively wrong: the directories do not overlap, the truncation made them look like they do.

What I hit

Running a container image with 31 overlay layers under bubblewrap 0.12.0:

bwrap: Can't make overlay mount on /... with options lowerdir=...: \
    Overlay directories may not overlap

The layers are all distinct sibling directories. Nothing overlaps.

What actually happened is that all the layers live under one layer store, so the paths shared a long prefix and each entry was about 132 bytes:

/oldroot/.../common-storage/overlay/<64 hex chars>/diff

31 of those plus lowerdir= and ,userxattr comes to about 4110 bytes. copy_mount_options() keeps PAGE_SIZE - 1 = 4095, so the 31st path was cut in the middle and became /oldroot/.../common-storage/overlay — the parent of all the other layers. At that point overlayfs is right: one lower layer really is an ancestor of the others. The overlap was manufactured by the truncation.

This cost a while to diagnose, even with the help of Claude Code, because the error message is accurate about what the kernel saw and says nothing about what went wrong.

Current state on main and the just released 0.13.0

Commit c77dd38 ("setup: Use O_PATH fds and disallow symlink targets") replaced the absolute paths with /proc/self/fd/N, which is about 18 bytes per layer instead of 132 (in my case). That raises the ceiling a lot, but it is still a ceiling, and it is still reported as something unrelated to the layer count.

Measured on 8e5e757, Linux 6.12, x86_64, unprivileged:

cd "$(mktemp -d)"
for i in $(seq 1 250); do mkdir -p "l$i"; done
args=$(for i in $(seq 1 250); do printf -- '--overlay-src %s/l%s ' "$PWD" "$i"; done)

bwrap --ro-bind / / $args --ro-overlay /mnt true
  • 230 layers: works.
  • 250 layers: fails.

The failure is a 4495-byte error message ending in:

bwrap: Can't make overlay mount on (null) with options lowerdir=/proc/self/fd/7:...
    ...:/proc/self/fd/256,userxattr: Invalid argument

Two separate problems visible in that one line:

  1. The destination prints as (null). The loop that collects the lower layers advances op to the last SETUP_OVERLAY_SRC, and those ops carry only a source, so op->dest is NULL by the time the error is formatted.
  2. The diagnosis is still wrong. Here the truncated /proc/self/fd/NNN happens to land on a different valid fd rather than on an ancestor directory, so the kernel says Invalid argument instead of complaining about an overlap. Which wrong answer you get depends only on what the cut path happens to name.

So on current main the limit is roughly 230 layers rather than roughly 30, but the shape of the bug is unchanged.

Why this is worth fixing rather than documenting

Container layer stores routinely produce tall stacks, and the failure mode scales badly: the more layers a user has, the more likely the truncated path lands on their shared layer-store parent, which is exactly the case that yields the confusing overlap message.

The layer paths are also not under the user's control in the common case — they come out of whatever produced the image — so "use shorter paths" is not a real workaround. Squashing layers is, but only if you know that is what the error means.

Suggested fix

fsconfig(FSCONFIG_SET_STRING, "lowerdir+", ...) appends one layer per call and accumulates the list inside the kernel, so there is no options string to overflow. Mounting the overlay with fsopen() + one fsconfig("lowerdir+") per layer + fsmount() + move_mount() removes the limit entirely; the only remaining bound is overlayfs's own OVL_MAX_STACK.

I confirmed this works for an ordinary unprivileged user inside unshare(CLONE_NEWUSER | CLONE_NEWNS), which is bubblewrap's model: 300, 400 and 499 layers all mount with every layer visible, and layer 500 fails with EINVAL, which is OVL_MAX_STACK. Unlike open_tree(OPEN_TREE_CLONE), fsopen() creates a new mount instead of cloning an existing one, so none of the cross-namespace restrictions apply.

overlayfs only accepts lowerdir+ from Linux 6.7 on, so older kernels need to keep the mount(2) path. On that path the length should at least be checked up front, so the error names the layer count instead of letting the kernel truncate and overlayfs guess.

This would be a third use of the new mount API after #754 / #756. It is unrelated to #755 beyond the shared theme: that one is about open_tree() for --[ro-]bind-fd and a path-lookup race, this one is a length limit. (I'm going to add a comment to #755 to report on my findings while trying to work that one out.)

I have a patch series implementing the above and will open a PR.

Version / environment

  • bubblewrap 0.12.0 for the original report; also reproduced on 8e5e757 (with a larger number of layers).
  • Linux 6.12, x86_64.
  • Unprivileged (user namespaces), not setuid.
Dominant language
C
Stars
8.8k
Forks
387
Avg merge
2d 18h
Merged PRs (30d)
17

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from containers/bubblewrap

All issues in containers/bubblewrap

Similar issues

More C issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.