Skip to content

Docker-in-gVisor fails with runc 1.3.6 due to unsupported tmpfs nr_blocks option #13670

Description

@tianyuzhou95

Description

Running Docker inside gVisor with runc 1.3.6 can fail when runc masks a
directory with a read-only tmpfs mount:

docker: Error response from daemon: failed to create task for container:
failed to create shim task: OCI runtime create failed: runc create failed:
unable to start container process: error during container init:
can't mask dir "/sys/firmware": mount src=tmpfs, dst=/sys/firmware,
flags=MS_RDONLY, data=nr_blocks=1,nr_inodes=1: invalid argument

This works with runc 1.3.5, but fails with runc 1.3.6. The relevant runc
change is:

opencontainers/runc@e0d23b4

The immediate failure is that gVisor tmpfs currently rejects the
nr_blocks=1 mount option.

Linux behavior

Current Linux tmpfs supports both size= and nr_blocks=. In
mm/shmem.c, both options update the same tmpfs block-limit field:

case Opt_size:
    size = memparse(param->string, &rest);
    if (*rest == '%') {
        size <<= PAGE_SHIFT;
        size *= totalram_pages();
        do_div(size, 100);
        rest++;
    }
    if (*rest)
        goto bad_value;
    ctx->blocks = DIV_ROUND_UP(size, PAGE_SIZE);
    ctx->seen |= SHMEM_SEEN_BLOCKS;
    break;
case Opt_nr_blocks:
    ctx->blocks = memparse(param->string, &rest);
    if (*rest || ctx->blocks > LONG_MAX)
        goto bad_value;
    ctx->seen |= SHMEM_SEEN_BLOCKS;
    break;

The mount data parser processes comma-separated options in input order
before handing each option to the filesystem parser. As a result,
size= and nr_blocks= are implicit aliases for the same state, and
the later option wins:

size=1m,nr_blocks=1  -> final block limit is 1
nr_blocks=1,size=1m  -> final block limit is 1 MiB rounded to pages

This is not an explicit precedence rule between the two option names; it
falls out of ordered parsing plus both options assigning ctx->blocks.

Relevant Linux source:

gVisor behavior

gVisor tmpfs currently parses mount data with
vfs.GenericParseMountOptions:

mopts := vfs.GenericParseMountOptions(opts.Data)

GenericParseMountOptions returns a map. It preserves last-wins behavior
for duplicate keys, but it does not preserve relative ordering across
different keys. This is fine for the currently supported tmpfs options,
but it means gVisor cannot exactly model Linux's implicit size= /
nr_blocks= overwrite behavior after parsing options into the map.

Today gVisor tmpfs supports size= and nr_inodes=, but not
nr_blocks=, so the runc 1.3.6 mount data fails with EINVAL.

Wait for discussion

A minimal compatibility fix could add nr_blocks= support. The open
question is what gVisor should do if both size= and nr_blocks= are
specified:

  • Preserve the current map parser and reject the combination as invalid,
    avoiding a silent gVisor-specific fixed precedence rule.
  • Refactor tmpfs option parsing to preserve input order, so gVisor can
    match Linux last-wins behavior exactly.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions