VYPR
High severityNVD Advisory· Published Apr 25, 2023· Updated Mar 5, 2025

Unpatched extfs vulnerabilities are exploitable through suid-mode Apptainer

CVE-2023-30549

Description

Apptainer is an open source container platform for Linux. There is an ext4 use-after-free flaw that is exploitable through versions of Apptainer < 1.1.0 and installations that include apptainer-suid < 1.1.8 on older operating systems where that CVE has not been patched. That includes Red Hat Enterprise Linux 7, Debian 10 buster (unless the linux-5.10 package is installed), Ubuntu 18.04 bionic and Ubuntu 20.04 focal. Use-after-free flaws in the kernel can be used to attack the kernel for denial of service and potentially for privilege escalation.

Apptainer 1.1.8 includes a patch that by default disables mounting of extfs filesystem types in setuid-root mode, while continuing to allow mounting of extfs filesystems in non-setuid "rootless" mode using fuse2fs.

Some workarounds are possible. Either do not install apptainer-suid (for versions 1.1.0 through 1.1.7) or set allow setuid = no in apptainer.conf. This requires having unprivileged user namespaces enabled and except for apptainer 1.1.x versions will disallow mounting of sif files, extfs files, and squashfs files in addition to other, less significant impacts. (Encrypted sif files are also not supported unprivileged in apptainer 1.1.x.). Alternatively, use the limit containers options in apptainer.conf/singularity.conf to limit sif files to trusted users, groups, and/or paths, and set allow container extfs = no to disallow mounting of extfs overlay files. The latter option by itself does not disallow mounting of extfs overlay partitions inside SIF files, so that's why the former options are also needed.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Apptainer versions prior to 1.1.8 can trigger a kernel use-after-free in ext4 during filesystem mounting, enabling local denial of service or privilege escalation.

Vulnerability

Description

CVE-2023-30549 describes a use-after-free vulnerability in the Linux kernel's ext4 filesystem that can be triggered through Apptainer, a container platform [1]. The flaw exists in the ext4 code and can be exploited when Apptainer mounts ext4 filesystem images. The vulnerability affects Apptainer versions before 1.1.0, and even in versions 1.1.0 through 1.1.7 when the apptainer-suid package is installed on older operating systems that have not patched the underlying kernel CVE-2022-1184 [2]. Impacted distributions include Red Hat Enterprise Linux 7, Debian 10 buster (unless linux-5.10 is installed), Ubuntu 18.04 bionic, and Ubuntu 20.04 focal.

Exploitation

Prerequisites and Mechanism

An attacker with local user privileges can exploit this vulnerability by using Apptainer to mount a specially crafted ext4 filesystem [1]. The attack requires that the kernel have the unpatched ext4 use-after-free bug (CVE-2022-1184) [2]. The vulnerability is triggered when Apptainer mounts an extfs filesystem in setuid-root mode, which allows a normal user to escalate privileges. The Apptainer project has mitigated the issue by disabling extfs mounting in setuid-root mode as of version 1.1.8, while permitting it in non-setuid "rootless" mode via fuse2fs [1].

Impact

Successful exploitation of the kernel use-after-free can lead to local denial of service or potential privilege escalation [1]. Because the flaw is in the kernel, an attacker gaining code execution could achieve arbitrary read/write on kernel memory, which often leads to full system compromise. The official description confirms that use-after-free bugs in the kernel can be used for privilege escalation [1].

Mitigation

Status

The Apptainer project has released version 1.1.8 which by default disables mounting of extfs filesystem types in setuid-root mode [1]. For users who cannot upgrade, workarounds include not installing apptainer-suid (for versions 1.1.0–1.1.7) or setting allow setuid = no in apptainer.conf [1]. Additionally, administrators can restrict containers to trusted users and paths using limit containers options and disable extfs overlay mounting with allow container extfs = no [1]. Users of affected old distributions should also apply kernel updates that address CVE-2022-1184, such as those found in Linux 5.4.243 and 4.19.283 [3][4].

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/apptainer/apptainerGo
< 1.1.81.1.8

Affected products

7

Patches

5
2220eaf90992

ext4: add bounds checking in get_max_inline_xattr_value_size()

https://github.com/torvalds/linuxTheodore Ts'oMay 12, 2023via ghsa
1 file changed · +11 1
  • fs/ext4/inline.c+11 1 modified
    @@ -34,6 +34,7 @@ static int get_max_inline_xattr_value_size(struct inode *inode,
     	struct ext4_xattr_ibody_header *header;
     	struct ext4_xattr_entry *entry;
     	struct ext4_inode *raw_inode;
    +	void *end;
     	int free, min_offs;
     
     	if (!EXT4_INODE_HAS_XATTR_SPACE(inode))
    @@ -57,14 +58,23 @@ static int get_max_inline_xattr_value_size(struct inode *inode,
     	raw_inode = ext4_raw_inode(iloc);
     	header = IHDR(inode, raw_inode);
     	entry = IFIRST(header);
    +	end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size;
     
     	/* Compute min_offs. */
    -	for (; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) {
    +	while (!IS_LAST_ENTRY(entry)) {
    +		void *next = EXT4_XATTR_NEXT(entry);
    +
    +		if (next >= end) {
    +			EXT4_ERROR_INODE(inode,
    +					 "corrupt xattr in inline inode");
    +			return 0;
    +		}
     		if (!entry->e_value_inum && entry->e_value_size) {
     			size_t offs = le16_to_cpu(entry->e_value_offs);
     			if (offs < min_offs)
     				min_offs = offs;
     		}
    +		entry = next;
     	}
     	free = min_offs -
     		((void *)entry - (void *)IFIRST(header)) - sizeof(__u32);
    
4f04351888a8

ext4: avoid a potential slab-out-of-bounds in ext4_group_desc_csum

https://github.com/torvalds/linuxTudor AmbarusMay 4, 2023via ghsa
1 file changed · +2 4
  • fs/ext4/super.c+2 4 modified
    @@ -3240,11 +3240,9 @@ static __le16 ext4_group_desc_csum(struct super_block *sb, __u32 block_group,
     	crc = crc16(crc, (__u8 *)gdp, offset);
     	offset += sizeof(gdp->bg_checksum); /* skip checksum */
     	/* for checksum of struct ext4_group_desc do the rest...*/
    -	if (ext4_has_feature_64bit(sb) &&
    -	    offset < le16_to_cpu(sbi->s_es->s_desc_size))
    +	if (ext4_has_feature_64bit(sb) && offset < sbi->s_desc_size)
     		crc = crc16(crc, (__u8 *)gdp + offset,
    -			    le16_to_cpu(sbi->s_es->s_desc_size) -
    -				offset);
    +			    sbi->s_desc_size - offset);
     
     out:
     	return cpu_to_le16(crc);
    
5a4964f5ba9c

Merge pull request from GHSA-j4rf-7357-f4cg

https://github.com/apptainer/apptainerDrDaveDApr 25, 2023via ghsa
8 files changed · +345 98
  • e2e/actions/actions.go+6 0 modified
    @@ -790,6 +790,9 @@ func (c actionTests) PersistentOverlay(t *testing.T) {
     		e2e.ExpectExit(0),
     	)
     
    +	e2e.SetDirective(t, c.env, "allow setuid-mount extfs", "yes")
    +	defer e2e.ResetDirective(t, c.env, "allow setuid-mount extfs")
    +
     	tests := []struct {
     		name    string
     		argv    []string
    @@ -1934,6 +1937,9 @@ func (c actionTests) bindImage(t *testing.T) {
     		e2e.ExpectExit(0),
     	)
     
    +	e2e.SetDirective(t, c.env, "allow setuid-mount extfs", "yes")
    +	defer e2e.ResetDirective(t, c.env, "allow setuid-mount extfs")
    +
     	tests := []struct {
     		name    string
     		profile e2e.Profile
    
  • e2e/config/config.go+196 46 modified
    @@ -25,14 +25,15 @@ import (
     )
     
     type configTests struct {
    -	env            e2e.TestEnv
    -	sifImage       string
    -	encryptedImage string
    -	squashfsImage  string
    -	ext3Image      string
    -	sandboxImage   string
    -	pemPublic      string
    -	pemPrivate     string
    +	env              e2e.TestEnv
    +	sifImage         string
    +	encryptedImage   string
    +	squashfsImage    string
    +	ext3Image        string
    +	ext3OverlayImage string
    +	sandboxImage     string
    +	pemPublic        string
    +	pemPrivate       string
     }
     
     // prepImages creates containers covering all image formats to test the
    @@ -96,6 +97,20 @@ func (c *configTests) prepImages(t *testing.T) (cleanup func(t *testing.T)) {
     		}
     	})
     
    +	// An ext3 overlay embedded in a SIF
    +	c.ext3OverlayImage = filepath.Join(tmpDir, "ext3Overlay.img")
    +	if err := fs.CopyFile(c.sifImage, c.ext3OverlayImage, 0o755); err != nil {
    +		t.Fatalf("Could not copy test image file: %v", err)
    +	}
    +	c.env.RunApptainer(
    +		t,
    +		e2e.AsSubtest("PrepareExt3Overlay"),
    +		e2e.WithProfile(e2e.UserProfile),
    +		e2e.WithCommand("overlay"),
    +		e2e.WithArgs("create", c.ext3OverlayImage),
    +		e2e.ExpectExit(0),
    +	)
    +
     	return cleanup
     }
     
    @@ -104,24 +119,8 @@ func (c configTests) configGlobal(t *testing.T) {
     	cleanup := c.prepImages(t)
     	defer cleanup(t)
     
    -	setDirective := func(t *testing.T, directive, value string) {
    -		c.env.RunApptainer(
    -			t,
    -			e2e.WithProfile(e2e.RootProfile),
    -			e2e.WithCommand("config global"),
    -			e2e.WithArgs("--set", directive, value),
    -			e2e.ExpectExit(0),
    -		)
    -	}
    -	resetDirective := func(t *testing.T, directive string) {
    -		c.env.RunApptainer(
    -			t,
    -			e2e.WithProfile(e2e.RootProfile),
    -			e2e.WithCommand("config global"),
    -			e2e.WithArgs("--reset", directive),
    -			e2e.ExpectExit(0),
    -		)
    -	}
    +	e2e.SetDirective(t, c.env, "allow setuid-mount extfs", "yes")
    +	defer e2e.ResetDirective(t, c.env, "allow setuid-mount extfs")
     
     	u := e2e.UserProfile.HostUser(t)
     	g, err := user.GetGrGID(u.GID)
    @@ -504,6 +503,169 @@ func (c configTests) configGlobal(t *testing.T) {
     			directiveValue: "yes",
     			exit:           0,
     		},
    +		// NOTE: the "allow setuid-mount" tests have to stay after the
    +		// "allow container" tests because they will be left in their
    +		// default settings which can interfere with "allow container" tests.
    +		{
    +			name:           "AllowSetuidMountEncryptedNo",
    +			argv:           []string{"--pem-path", c.pemPrivate, c.encryptedImage, "true"},
    +			profile:        e2e.UserProfile,
    +			directive:      "allow setuid-mount encrypted",
    +			directiveValue: "no",
    +			exit:           255,
    +		},
    +		{
    +			name:           "AllowSetuidMountEncryptedYes",
    +			argv:           []string{"--pem-path", c.pemPrivate, c.encryptedImage, "true"},
    +			profile:        e2e.UserProfile,
    +			directive:      "allow setuid-mount encrypted",
    +			directiveValue: "yes",
    +			exit:           0,
    +		},
    +		{
    +			name:           "AllowSetuidMountSquashfsNo",
    +			argv:           []string{c.squashfsImage, "true"},
    +			profile:        e2e.UserProfile,
    +			directive:      "allow setuid-mount squashfs",
    +			directiveValue: "no",
    +			exit:           255,
    +		},
    +		{
    +			name:           "AllowSetuidMountSquashfsNoSif",
    +			argv:           []string{c.sifImage, "true"},
    +			profile:        e2e.UserProfile,
    +			directive:      "allow setuid-mount squashfs",
    +			directiveValue: "no",
    +			exit:           255,
    +		},
    +		{
    +			name:           "AllowSetuidMountSquashfsNoBind",
    +			argv:           []string{"-B", c.squashfsImage + ":/sqsh:image-src=/", c.sifImage, "true"},
    +			profile:        e2e.UserProfile,
    +			directive:      "allow setuid-mount squashfs",
    +			directiveValue: "no",
    +			exit:           255,
    +		},
    +		{
    +			name:           "AllowSetuidMountSquashfsNoUserns",
    +			argv:           []string{c.squashfsImage, "true"},
    +			profile:        e2e.UserNamespaceProfile,
    +			directive:      "allow setuid-mount squashfs",
    +			directiveValue: "no",
    +			exit:           0,
    +		},
    +		{
    +			name:           "AllowSetuidMountSquashfsNoUsernsSif",
    +			argv:           []string{c.sifImage, "true"},
    +			profile:        e2e.UserNamespaceProfile,
    +			directive:      "allow setuid-mount squashfs",
    +			directiveValue: "no",
    +			exit:           0,
    +		},
    +		{
    +			name:           "AllowSetuidMountSquashfsNoUsernsBind",
    +			argv:           []string{"-B", c.squashfsImage + ":/sqsh:image-src=/", c.sifImage, "true"},
    +			profile:        e2e.UserNamespaceProfile,
    +			directive:      "allow setuid-mount squashfs",
    +			directiveValue: "no",
    +			exit:           0,
    +		},
    +		{
    +			name:           "AllowSetuidMountSquashfsYes",
    +			argv:           []string{c.squashfsImage, "true"},
    +			profile:        e2e.UserProfile,
    +			directive:      "allow setuid-mount squashfs",
    +			directiveValue: "yes",
    +			exit:           0,
    +		},
    +		{
    +			name:           "AllowSetuidMountSquashfsYesSif",
    +			argv:           []string{c.sifImage, "true"},
    +			profile:        e2e.UserProfile,
    +			directive:      "allow setuid-mount squashfs",
    +			directiveValue: "yes",
    +			exit:           0,
    +		},
    +		{
    +			name:           "AllowSetuidMountSquashfsYesBind",
    +			argv:           []string{"-B", c.squashfsImage + ":/sqsh:image-src=/", c.sifImage, "true"},
    +			profile:        e2e.UserProfile,
    +			directive:      "allow setuid-mount squashfs",
    +			directiveValue: "yes",
    +			exit:           0,
    +		},
    +		{
    +			name:           "AllowSetuidMountExtfsNo",
    +			argv:           []string{c.ext3Image, "true"},
    +			profile:        e2e.UserProfile,
    +			directive:      "allow setuid-mount extfs",
    +			directiveValue: "no",
    +			exit:           255,
    +		},
    +		{
    +			name:           "AllowSetuidMountExtfsNoSif",
    +			argv:           []string{c.ext3OverlayImage, "true"},
    +			profile:        e2e.UserProfile,
    +			directive:      "allow setuid-mount extfs",
    +			directiveValue: "no",
    +			exit:           255,
    +		},
    +		{
    +			name:           "AllowSetuidMountExtfsNoBind",
    +			argv:           []string{"-B", c.ext3Image + ":/ext3:image-src=/", c.sifImage, "true"},
    +			profile:        e2e.UserProfile,
    +			directive:      "allow setuid-mount extfs",
    +			directiveValue: "no",
    +			exit:           255,
    +		},
    +		{
    +			name:           "AllowSetuidMountExtfsNoUserns",
    +			argv:           []string{c.ext3Image, "true"},
    +			profile:        e2e.UserNamespaceProfile,
    +			directive:      "allow setuid-mount extfs",
    +			directiveValue: "no",
    +			exit:           0,
    +		},
    +		{
    +			name:           "AllowSetuidMountExtfsNoUsernsSif",
    +			argv:           []string{c.ext3OverlayImage, "true"},
    +			profile:        e2e.UserNamespaceProfile,
    +			directive:      "allow setuid-mount extfs",
    +			directiveValue: "no",
    +			exit:           0,
    +		},
    +		{
    +			name:           "AllowSetuidMountExtfsNoUsernsBind",
    +			argv:           []string{"-B", c.ext3Image + ":/ext3:image-src=/", c.sifImage, "true"},
    +			profile:        e2e.UserNamespaceProfile,
    +			directive:      "allow setuid-mount extfs",
    +			directiveValue: "no",
    +			exit:           0,
    +		},
    +		{
    +			name:           "AllowSetuidMountExtfsYes",
    +			argv:           []string{c.ext3Image, "true"},
    +			profile:        e2e.UserProfile,
    +			directive:      "allow setuid-mount extfs",
    +			directiveValue: "yes",
    +			exit:           0,
    +		},
    +		{
    +			name:           "AllowSetuidMountExtfsYesSif",
    +			argv:           []string{c.ext3OverlayImage, "true"},
    +			profile:        e2e.UserProfile,
    +			directive:      "allow setuid-mount extfs",
    +			directiveValue: "yes",
    +			exit:           0,
    +		},
    +		{
    +			name:           "AllowSetuidMountExtfsYesBind",
    +			argv:           []string{"-B", c.ext3Image + ":/ext3:image-src=/", c.sifImage, "true"},
    +			profile:        e2e.UserProfile,
    +			directive:      "allow setuid-mount extfs",
    +			directiveValue: "yes",
    +			exit:           0,
    +		},
     		// FIXME
     		// The e2e tests currently run inside a PID namespace.
     		//   (see internal/init/init_linux.go)
    @@ -537,10 +699,10 @@ func (c configTests) configGlobal(t *testing.T) {
     				if tt.addRequirementsFn != nil {
     					tt.addRequirementsFn(t)
     				}
    -				setDirective(t, tt.directive, tt.directiveValue)
    +				e2e.SetDirective(t, c.env, tt.directive, tt.directiveValue)
     			}),
     			e2e.PostRun(func(t *testing.T) {
    -				resetDirective(t, tt.directive)
    +				e2e.ResetDirective(t, c.env, tt.directive)
     			}),
     			e2e.WithCommand("exec"),
     			e2e.WithArgs(tt.argv...),
    @@ -553,26 +715,14 @@ func (c configTests) configGlobal(t *testing.T) {
     func (c configTests) configGlobalCombination(t *testing.T) {
     	e2e.EnsureImage(t, c.env)
     
    -	setDirective := func(t *testing.T, directives map[string]string) {
    +	setDirectives := func(t *testing.T, directives map[string]string) {
     		for k, v := range directives {
    -			c.env.RunApptainer(
    -				t,
    -				e2e.WithProfile(e2e.RootProfile),
    -				e2e.WithCommand("config global"),
    -				e2e.WithArgs("--set", k, v),
    -				e2e.ExpectExit(0),
    -			)
    +			e2e.SetDirective(t, c.env, k, v)
     		}
     	}
    -	resetDirective := func(t *testing.T, directives map[string]string) {
    +	resetDirectives := func(t *testing.T, directives map[string]string) {
     		for k := range directives {
    -			c.env.RunApptainer(
    -				t,
    -				e2e.WithProfile(e2e.RootProfile),
    -				e2e.WithCommand("config global"),
    -				e2e.WithArgs("--reset", k),
    -				e2e.ExpectExit(0),
    -			)
    +			e2e.ResetDirective(t, c.env, k)
     		}
     	}
     
    @@ -741,10 +891,10 @@ func (c configTests) configGlobalCombination(t *testing.T) {
     				if tt.addRequirementsFn != nil {
     					tt.addRequirementsFn(t)
     				}
    -				setDirective(t, tt.directives)
    +				setDirectives(t, tt.directives)
     			}),
     			e2e.PostRun(func(t *testing.T) {
    -				resetDirective(t, tt.directives)
    +				resetDirectives(t, tt.directives)
     			}),
     			e2e.WithCommand("exec"),
     			e2e.WithArgs(tt.argv...),
    
  • e2e/internal/e2e/config.go+20 0 modified
    @@ -43,3 +43,23 @@ func SetupDefaultConfig(t *testing.T, path string) {
     		}
     	})(t)
     }
    +
    +func SetDirective(t *testing.T, env TestEnv, directive, value string) {
    +	env.RunApptainer(
    +		t,
    +		WithProfile(RootProfile),
    +		WithCommand("config global"),
    +		WithArgs("--set", directive, value),
    +		ExpectExit(0),
    +	)
    +}
    +
    +func ResetDirective(t *testing.T, env TestEnv, directive string) {
    +	env.RunApptainer(
    +		t,
    +		WithProfile(RootProfile),
    +		WithCommand("config global"),
    +		WithArgs("--reset", directive),
    +		ExpectExit(0),
    +	)
    +}
    
  • e2e/overlay/overlay.go+3 0 modified
    @@ -74,6 +74,9 @@ func (c ctx) testOverlayCreate(t *testing.T) {
     		e2e.ExpectExit(0),
     	)
     
    +	e2e.SetDirective(t, c.env, "allow setuid-mount extfs", "yes")
    +	defer e2e.ResetDirective(t, c.env, "allow setuid-mount extfs")
    +
     	type test struct {
     		name    string
     		profile e2e.Profile
    
  • e2e/run/run.go+3 0 modified
    @@ -286,6 +286,9 @@ func (c ctx) testFuseExt3Mount(t *testing.T) {
     		t.Fatalf(err.Error())
     	}
     
    +	e2e.SetDirective(t, c.env, "allow setuid-mount extfs", "yes")
    +	defer e2e.ResetDirective(t, c.env, "allow setuid-mount extfs")
    +
     	c.env.RunApptainer(
     		t,
     		e2e.WithProfile(e2e.UserProfile),
    
  • internal/pkg/runtime/engine/apptainer/prepare_linux.go+29 11 modified
    @@ -150,7 +150,7 @@ func (e *EngineOperations) PrepareConfig(starterConfig *starter.Config) error {
     		if err := e.prepareContainerConfig(starterConfig); err != nil {
     			return err
     		}
    -		if err := e.loadImages(starterConfig); err != nil {
    +		if err := e.loadImages(starterConfig, userNS); err != nil {
     			return err
     		}
     	}
    @@ -1131,12 +1131,12 @@ func (e *EngineOperations) setSessionLayer(img *image.Image) error {
     	return nil
     }
     
    -func (e *EngineOperations) loadImages(starterConfig *starter.Config) error {
    +func (e *EngineOperations) loadImages(starterConfig *starter.Config, userNS bool) error {
     	images := make([]image.Image, 0)
     
     	// load rootfs image
     	writable := e.EngineConfig.GetWritableImage()
    -	img, err := e.loadImage(e.EngineConfig.GetImage(), writable)
    +	img, err := e.loadImage(e.EngineConfig.GetImage(), writable, userNS)
     	if err != nil {
     		return err
     	}
    @@ -1236,7 +1236,13 @@ func (e *EngineOperations) loadImages(starterConfig *starter.Config) error {
     				return fmt.Errorf("while getting overlay partitions in %s: %s", img.Path, err)
     			}
     			for _, p := range overlays {
    -				if img.Writable && p.Type == image.EXT3 {
    +				if p.Type != image.EXT3 {
    +					continue
    +				}
    +				if !userNS && !e.EngineConfig.File.AllowSetuidMountExtfs {
    +					return fmt.Errorf("configuration disallows users from mounting SIF extfs partition in setuid mode, try --userns")
    +				}
    +				if img.Writable {
     					writableOverlayPath = img.Path
     				}
     			}
    @@ -1252,7 +1258,7 @@ func (e *EngineOperations) loadImages(starterConfig *starter.Config) error {
     
     	switch e.EngineConfig.GetSessionLayer() {
     	case apptainerConfig.OverlayLayer:
    -		overlayImages, err := e.loadOverlayImages(starterConfig, writableOverlayPath)
    +		overlayImages, err := e.loadOverlayImages(starterConfig, writableOverlayPath, userNS)
     		if err != nil {
     			return fmt.Errorf("while loading overlay images: %s", err)
     		}
    @@ -1264,7 +1270,7 @@ func (e *EngineOperations) loadImages(starterConfig *starter.Config) error {
     		}
     	}
     
    -	bindImages, err := e.loadBindImages(starterConfig)
    +	bindImages, err := e.loadBindImages(starterConfig, userNS)
     	if err != nil {
     		return fmt.Errorf("while loading data bind images: %s", err)
     	}
    @@ -1276,7 +1282,7 @@ func (e *EngineOperations) loadImages(starterConfig *starter.Config) error {
     }
     
     // loadOverlayImages loads overlay images.
    -func (e *EngineOperations) loadOverlayImages(starterConfig *starter.Config, writableOverlayPath string) ([]image.Image, error) {
    +func (e *EngineOperations) loadOverlayImages(starterConfig *starter.Config, writableOverlayPath string, userNS bool) ([]image.Image, error) {
     	images := make([]image.Image, 0)
     
     	for _, overlayImg := range e.EngineConfig.GetOverlayImage() {
    @@ -1289,7 +1295,7 @@ func (e *EngineOperations) loadOverlayImages(starterConfig *starter.Config, writ
     			}
     		}
     
    -		img, err := e.loadImage(splitted[0], writableOverlay)
    +		img, err := e.loadImage(splitted[0], writableOverlay, userNS)
     		if err != nil {
     			if !image.IsReadOnlyFilesytem(err) {
     				return nil, fmt.Errorf("failed to open overlay image %s: %s", splitted[0], err)
    @@ -1325,7 +1331,7 @@ func (e *EngineOperations) loadOverlayImages(starterConfig *starter.Config, writ
     }
     
     // loadBindImages load data bind images.
    -func (e *EngineOperations) loadBindImages(starterConfig *starter.Config) ([]image.Image, error) {
    +func (e *EngineOperations) loadBindImages(starterConfig *starter.Config, userNS bool) ([]image.Image, error) {
     	images := make([]image.Image, 0)
     
     	binds := e.EngineConfig.GetBindPath()
    @@ -1339,7 +1345,7 @@ func (e *EngineOperations) loadBindImages(starterConfig *starter.Config) ([]imag
     
     		sylog.Debugf("Loading data image %s", imagePath)
     
    -		img, err := e.loadImage(imagePath, !binds[i].Readonly())
    +		img, err := e.loadImage(imagePath, !binds[i].Readonly(), userNS)
     		if err != nil && !image.IsReadOnlyFilesytem(err) {
     			return nil, fmt.Errorf("failed to load data image %s: %s", imagePath, err)
     		}
    @@ -1355,7 +1361,7 @@ func (e *EngineOperations) loadBindImages(starterConfig *starter.Config) ([]imag
     	return images, nil
     }
     
    -func (e *EngineOperations) loadImage(path string, writable bool) (*image.Image, error) {
    +func (e *EngineOperations) loadImage(path string, writable bool, userNS bool) (*image.Image, error) {
     	const delSuffix = " (deleted)"
     
     	imgObject, imgErr := image.Init(path, writable)
    @@ -1413,18 +1419,27 @@ func (e *EngineOperations) loadImage(path string, writable bool) (*image.Image,
     		if !e.EngineConfig.File.AllowContainerSquashfs {
     			return nil, fmt.Errorf("configuration disallows users from running squashFS containers")
     		}
    +		if !userNS && !e.EngineConfig.File.AllowSetuidMountSquashfs {
    +			return nil, fmt.Errorf("configuration disallows users from mounting squashFS in setuid mode, try --userns")
    +		}
     	// Bare EXT3
     	case image.EXT3:
     		if !e.EngineConfig.File.AllowContainerExtfs {
     			return nil, fmt.Errorf("configuration disallows users from running extFS containers")
     		}
    +		if !userNS && !e.EngineConfig.File.AllowSetuidMountExtfs {
    +			return nil, fmt.Errorf("configuration disallows users from mounting extfs in setuid mode, try --userns")
    +		}
     	// Bare sandbox directory
     	case image.SANDBOX:
     		if !e.EngineConfig.File.AllowContainerDir {
     			return nil, fmt.Errorf("configuration disallows users from running sandbox containers")
     		}
     	// SIF
     	case image.SIF:
    +		if !userNS && !e.EngineConfig.File.AllowSetuidMountSquashfs {
    +			return nil, fmt.Errorf("configuration disallows users from mounting SIF squashFS partition in setuid mode, try --userns")
    +		}
     		// Check if SIF contains an encrypted rootfs partition.
     		// We don't support encryption for other partitions at present.
     		encrypted, err := imgObject.HasEncryptedRootFs()
    @@ -1435,6 +1450,9 @@ func (e *EngineOperations) loadImage(path string, writable bool) (*image.Image,
     		if encrypted && !e.EngineConfig.File.AllowContainerEncrypted {
     			return nil, fmt.Errorf("configuration disallows users from running encrypted SIF containers")
     		}
    +		if encrypted && !userNS && !e.EngineConfig.File.AllowSetuidMountEncrypted {
    +			return nil, fmt.Errorf("configuration disallows users from mounting encrypted files in setuid mode")
    +		}
     		// SIF without encryption - regardless of rootfs filesystem type
     		if !encrypted && !e.EngineConfig.File.AllowContainerSIF {
     			return nil, fmt.Errorf("configuration disallows users from running unencrypted SIF containers")
    
  • pkg/util/apptainerconf/config.go+84 41 modified
    @@ -76,46 +76,49 @@ func SetBinaryPath(libexecDir string, nonSuid bool) {
     
     // File describes the apptainer.conf file options
     type File struct {
    -	AllowSetuid             bool     `default:"yes" authorized:"yes,no" directive:"allow setuid"`
    -	AllowPidNs              bool     `default:"yes" authorized:"yes,no" directive:"allow pid ns"`
    -	ConfigPasswd            bool     `default:"yes" authorized:"yes,no" directive:"config passwd"`
    -	ConfigGroup             bool     `default:"yes" authorized:"yes,no" directive:"config group"`
    -	ConfigResolvConf        bool     `default:"yes" authorized:"yes,no" directive:"config resolv_conf"`
    -	MountProc               bool     `default:"yes" authorized:"yes,no" directive:"mount proc"`
    -	MountSys                bool     `default:"yes" authorized:"yes,no" directive:"mount sys"`
    -	MountDevPts             bool     `default:"yes" authorized:"yes,no" directive:"mount devpts"`
    -	MountHome               bool     `default:"yes" authorized:"yes,no" directive:"mount home"`
    -	MountTmp                bool     `default:"yes" authorized:"yes,no" directive:"mount tmp"`
    -	MountHostfs             bool     `default:"no" authorized:"yes,no" directive:"mount hostfs"`
    -	UserBindControl         bool     `default:"yes" authorized:"yes,no" directive:"user bind control"`
    -	EnableFusemount         bool     `default:"yes" authorized:"yes,no" directive:"enable fusemount"`
    -	EnableUnderlay          bool     `default:"yes" authorized:"yes,no" directive:"enable underlay"`
    -	MountSlave              bool     `default:"yes" authorized:"yes,no" directive:"mount slave"`
    -	AllowContainerSIF       bool     `default:"yes" authorized:"yes,no" directive:"allow container sif"`
    -	AllowContainerEncrypted bool     `default:"yes" authorized:"yes,no" directive:"allow container encrypted"`
    -	AllowContainerSquashfs  bool     `default:"yes" authorized:"yes,no" directive:"allow container squashfs"`
    -	AllowContainerExtfs     bool     `default:"yes" authorized:"yes,no" directive:"allow container extfs"`
    -	AllowContainerDir       bool     `default:"yes" authorized:"yes,no" directive:"allow container dir"`
    -	AlwaysUseNv             bool     `default:"no" authorized:"yes,no" directive:"always use nv"`
    -	UseNvCCLI               bool     `default:"no" authorized:"yes,no" directive:"use nvidia-container-cli"`
    -	AlwaysUseRocm           bool     `default:"no" authorized:"yes,no" directive:"always use rocm"`
    -	SharedLoopDevices       bool     `default:"no" authorized:"yes,no" directive:"shared loop devices"`
    -	MaxLoopDevices          uint     `default:"256" directive:"max loop devices"`
    -	SessiondirMaxSize       uint     `default:"16" directive:"sessiondir max size"`
    -	MountDev                string   `default:"yes" authorized:"yes,no,minimal" directive:"mount dev"`
    -	EnableOverlay           string   `default:"try" authorized:"yes,no,try,driver" directive:"enable overlay"`
    -	BindPath                []string `default:"/etc/localtime,/etc/hosts" directive:"bind path"`
    -	LimitContainerOwners    []string `directive:"limit container owners"`
    -	LimitContainerGroups    []string `directive:"limit container groups"`
    -	LimitContainerPaths     []string `directive:"limit container paths"`
    -	AllowNetUsers           []string `directive:"allow net users"`
    -	AllowNetGroups          []string `directive:"allow net groups"`
    -	AllowNetNetworks        []string `directive:"allow net networks"`
    -	RootDefaultCapabilities string   `default:"full" authorized:"full,file,no" directive:"root default capabilities"`
    -	MemoryFSType            string   `default:"tmpfs" authorized:"tmpfs,ramfs" directive:"memory fs type"`
    -	CniConfPath             string   `directive:"cni configuration path"`
    -	CniPluginPath           string   `directive:"cni plugin path"`
    -	BinaryPath              string   `default:"$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" directive:"binary path"`
    +	AllowSetuid               bool     `default:"yes" authorized:"yes,no" directive:"allow setuid"`
    +	AllowPidNs                bool     `default:"yes" authorized:"yes,no" directive:"allow pid ns"`
    +	ConfigPasswd              bool     `default:"yes" authorized:"yes,no" directive:"config passwd"`
    +	ConfigGroup               bool     `default:"yes" authorized:"yes,no" directive:"config group"`
    +	ConfigResolvConf          bool     `default:"yes" authorized:"yes,no" directive:"config resolv_conf"`
    +	MountProc                 bool     `default:"yes" authorized:"yes,no" directive:"mount proc"`
    +	MountSys                  bool     `default:"yes" authorized:"yes,no" directive:"mount sys"`
    +	MountDevPts               bool     `default:"yes" authorized:"yes,no" directive:"mount devpts"`
    +	MountHome                 bool     `default:"yes" authorized:"yes,no" directive:"mount home"`
    +	MountTmp                  bool     `default:"yes" authorized:"yes,no" directive:"mount tmp"`
    +	MountHostfs               bool     `default:"no" authorized:"yes,no" directive:"mount hostfs"`
    +	UserBindControl           bool     `default:"yes" authorized:"yes,no" directive:"user bind control"`
    +	EnableFusemount           bool     `default:"yes" authorized:"yes,no" directive:"enable fusemount"`
    +	EnableUnderlay            bool     `default:"yes" authorized:"yes,no" directive:"enable underlay"`
    +	MountSlave                bool     `default:"yes" authorized:"yes,no" directive:"mount slave"`
    +	AllowContainerSIF         bool     `default:"yes" authorized:"yes,no" directive:"allow container sif"`
    +	AllowContainerEncrypted   bool     `default:"yes" authorized:"yes,no" directive:"allow container encrypted"`
    +	AllowContainerSquashfs    bool     `default:"yes" authorized:"yes,no" directive:"allow container squashfs"`
    +	AllowContainerExtfs       bool     `default:"yes" authorized:"yes,no" directive:"allow container extfs"`
    +	AllowContainerDir         bool     `default:"yes" authorized:"yes,no" directive:"allow container dir"`
    +	AllowSetuidMountEncrypted bool     `default:"yes" authorized:"yes,no" directive:"allow setuid-mount encrypted"`
    +	AllowSetuidMountSquashfs  bool     `default:"yes" authorized:"yes,no" directive:"allow setuid-mount squashfs"`
    +	AllowSetuidMountExtfs     bool     `default:"no" authorized:"yes,no" directive:"allow setuid-mount extfs"`
    +	AlwaysUseNv               bool     `default:"no" authorized:"yes,no" directive:"always use nv"`
    +	UseNvCCLI                 bool     `default:"no" authorized:"yes,no" directive:"use nvidia-container-cli"`
    +	AlwaysUseRocm             bool     `default:"no" authorized:"yes,no" directive:"always use rocm"`
    +	SharedLoopDevices         bool     `default:"no" authorized:"yes,no" directive:"shared loop devices"`
    +	MaxLoopDevices            uint     `default:"256" directive:"max loop devices"`
    +	SessiondirMaxSize         uint     `default:"16" directive:"sessiondir max size"`
    +	MountDev                  string   `default:"yes" authorized:"yes,no,minimal" directive:"mount dev"`
    +	EnableOverlay             string   `default:"try" authorized:"yes,no,try,driver" directive:"enable overlay"`
    +	BindPath                  []string `default:"/etc/localtime,/etc/hosts" directive:"bind path"`
    +	LimitContainerOwners      []string `directive:"limit container owners"`
    +	LimitContainerGroups      []string `directive:"limit container groups"`
    +	LimitContainerPaths       []string `directive:"limit container paths"`
    +	AllowNetUsers             []string `directive:"allow net users"`
    +	AllowNetGroups            []string `directive:"allow net groups"`
    +	AllowNetNetworks          []string `directive:"allow net networks"`
    +	RootDefaultCapabilities   string   `default:"full" authorized:"full,file,no" directive:"root default capabilities"`
    +	MemoryFSType              string   `default:"tmpfs" authorized:"tmpfs,ramfs" directive:"memory fs type"`
    +	CniConfPath               string   `directive:"cni configuration path"`
    +	CniPluginPath             string   `directive:"cni plugin path"`
    +	BinaryPath                string   `default:"$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" directive:"binary path"`
     	// SuidBinaryPath is hidden; it is not referenced below, and overwritten
     	SuidBinaryPath      string `directive:"suidbinary path"`
     	MksquashfsProcs     uint   `default:"0" directive:"mksquashfs procs"`
    @@ -127,6 +130,18 @@ type File struct {
     	SystemdCgroups      bool   `default:"yes" authorized:"yes,no" directive:"systemd cgroups"`
     }
     
    +// NOTE: if you think that we may want to change the default for any
    +// configuration parameter in the future, it is a good idea to conditionally
    +// insert a comment before the default setting when the setting is equal
    +// to the current default.  That enables the defaults to get updated in
    +// a new release even if an administrator has changed one of the *other*
    +// settings.  This gets around the problem of packagers such as rpm
    +// refusing to overwrite a configuration file if any change has been made.
    +// This technique is used for example in the "allow setuid-mount" options
    +// below.  If a default is changed in a future release, both the default
    +// setting above and the expression for the conditional comment below need
    +// to change at the same time.
    +
     const TemplateAsset = `# APPTAINER.CONF
     # This is the global configuration file for Apptainer. This file controls
     # what the container is allowed to do on a particular host, and as a result
    @@ -321,7 +336,9 @@ sessiondir max size = {{ .SessiondirMaxSize }}
     # ALLOW CONTAINER ${TYPE}: [BOOL]
     # DEFAULT: yes
     # This feature limits what kind of containers that Apptainer will allow
    -# users to use (note this does not apply for root).
    +# users to use (note this does not apply for root).  Note that some of the
    +# same operations can be limited in setuid mode by the ALLOW SETUID-MOUNT
    +# feature below; both types need to be "yes" to be allowed.
     #
     # Allow use of unencrypted SIF containers
     allow container sif = {{ if eq .AllowContainerSIF true}}yes{{ else }}no{{ end }}
    @@ -334,6 +351,32 @@ allow container squashfs = {{ if eq .AllowContainerSquashfs true }}yes{{ else }}
     allow container extfs = {{ if eq .AllowContainerExtfs true }}yes{{ else }}no{{ end }}
     allow container dir = {{ if eq .AllowContainerDir true }}yes{{ else }}no{{ end }}
     
    +# ALLOW SETUID-MOUNT ${TYPE}: [BOOL]
    +# DEFAULT: yes, except no for extfs
    +# This feature limits what types of mounts that Apptainer will allow
    +# unprivileged users to use in setuid mode.  Normally these operations
    +# require the elevated privileges of setuid mode, although Apptainer
    +# has unprivileged alternatives for squashfs and extfs.  Note that some of
    +# the same operations can also be limited by the ALLOW CONTAINER feature
    +# above; both types need to be "yes" to be allowed.
    +#
    +# Allow mounting of SIF encryption (using the kernel device-mapper) in
    +# setuid mode
    +{{ if eq .AllowSetuidMountEncrypted true}}# {{ end }}allow setuid-mount encrypted = {{ if eq .AllowSetuidMountEncrypted true}}yes{{ else }}no{{ end }}
    +#
    +# Allow mounting of squashfs filesystem types in setuid mode, both inside and
    +# outside of SIF files
    +{{ if eq .AllowSetuidMountSquashfs true}}# {{ end }}allow setuid-mount squashfs = {{ if eq .AllowSetuidMountSquashfs true}}yes{{ else }}no{{ end }}
    +#
    +# Allow mounting of extfs filesystem types in setuid mode, both inside and
    +# outside of SIF files.  WARNING: this filesystem type frequently has relevant
    +# CVEs that that take a very long time for vendors to patch because they are
    +# not considered to be High severity since normally unprivileged users do
    +# not have write access to the raw filesystem data.  This is why this option
    +# defaults to "no".  Change it at your own risk and consider using the
    +# LIMIT CONTAINER features above if you do.
    +{{ if eq .AllowSetuidMountExtfs false}}# {{ end }}allow setuid-mount extfs = {{ if eq .AllowSetuidMountExtfs true}}yes{{ else }}no{{ end }}
    +
     # ALLOW NET USERS: [STRING]
     # DEFAULT: NULL
     # Allow specified root administered CNI network configurations to be used by the
    
  • scripts/should-e2e-run+4 0 modified
    @@ -83,6 +83,10 @@ case "${TARGET_BRANCH}" in
     		require_e2e=true
     		;;
     
    +	null)
    +		# Failed to read api, could be private repo.  Run tests.
    +		require_e2e=true
    +		;;
     	*)
     		# The branch is not master or release, skip e2e
     		require_e2e=false
    
61a1d87a324a

ext4: fix check for block being out of directory size

https://github.com/torvalds/linuxJan KaraAug 22, 2022via ghsa
1 file changed · +1 1
  • fs/ext4/namei.c+1 1 modified
    @@ -126,7 +126,7 @@ static struct buffer_head *__ext4_read_dirblock(struct inode *inode,
     	struct ext4_dir_entry *dirent;
     	int is_dx_block = 0;
     
    -	if (block >= inode->i_size) {
    +	if (block >= inode->i_size >> inode->i_blkbits) {
     		ext4_error_inode(inode, func, line, block,
     		       "Attempting to read directory block (%u) that is past i_size (%llu)",
     		       block, inode->i_size);
    
65f8ea4cd57d

ext4: check if directory block is within i_size

https://github.com/torvalds/linuxLukas CzernerJul 4, 2022via ghsa
1 file changed · +7 0
  • fs/ext4/namei.c+7 0 modified
    @@ -110,6 +110,13 @@ static struct buffer_head *__ext4_read_dirblock(struct inode *inode,
     	struct ext4_dir_entry *dirent;
     	int is_dx_block = 0;
     
    +	if (block >= inode->i_size) {
    +		ext4_error_inode(inode, func, line, block,
    +		       "Attempting to read directory block (%u) that is past i_size (%llu)",
    +		       block, inode->i_size);
    +		return ERR_PTR(-EFSCORRUPTED);
    +	}
    +
     	if (ext4_simulate_fail(inode->i_sb, EXT4_SIM_DIRBLOCK_EIO))
     		bh = ERR_PTR(-EIO);
     	else
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

21

News mentions

0

No linked articles in our index yet.