Skip to content

Commit 85dabc8

Browse files
Merge pull request #5946 from dashea/dshea-1.27-CVE-2024-11218
[release-1.27] Backport fix for CVE-2024-11218
2 parents a64c647 + 2a43238 commit 85dabc8

27 files changed

+1299
-248
lines changed

cmd/buildah/run.go

+34-30
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ import (
88

99
"github.com/containers/buildah"
1010
internalParse "github.com/containers/buildah/internal/parse"
11+
internalUtil "github.com/containers/buildah/internal/util"
1112
buildahcli "github.com/containers/buildah/pkg/cli"
13+
"github.com/containers/buildah/pkg/overlay"
1214
"github.com/containers/buildah/pkg/parse"
1315
"github.com/containers/buildah/util"
14-
"github.com/containers/storage/pkg/lockfile"
16+
"github.com/containers/storage/pkg/mount"
1517
"github.com/sirupsen/logrus"
1618
"github.com/spf13/cobra"
1719
)
@@ -101,6 +103,16 @@ func runCmd(c *cobra.Command, args []string, iopts runInputOptions) error {
101103
return errors.New("command must be specified")
102104
}
103105

106+
tmpDir, err := os.MkdirTemp(internalUtil.GetTempDir(), "buildahvolume")
107+
if err != nil {
108+
return fmt.Errorf("creating temporary directory: %w", err)
109+
}
110+
defer func() {
111+
if err := os.Remove(tmpDir); err != nil {
112+
logrus.Debugf("removing should-be-empty temporary directory %q: %v", tmpDir, err)
113+
}
114+
}()
115+
104116
store, err := getStore(c)
105117
if err != nil {
106118
return err
@@ -159,13 +171,31 @@ func runCmd(c *cobra.Command, args []string, iopts runInputOptions) error {
159171
if err != nil {
160172
return fmt.Errorf("error building system context: %w", err)
161173
}
162-
mounts, mountedImages, lockedTargets, err := internalParse.GetVolumes(systemContext, store, iopts.volumes, iopts.mounts, iopts.contextDir)
174+
mounts, mountedImages, intermediateMounts, _, lockedTargets, err := internalParse.GetVolumes(systemContext, store, builder.MountLabel, iopts.volumes, iopts.mounts, iopts.contextDir, tmpDir)
163175
if err != nil {
164176
return err
165177
}
178+
defer func() {
179+
if err := overlay.CleanupContent(tmpDir); err != nil {
180+
logrus.Debugf("unmounting overlay mounts under %q: %v", tmpDir, err)
181+
}
182+
for _, intermediateMount := range intermediateMounts {
183+
if err := mount.Unmount(intermediateMount); err != nil {
184+
logrus.Debugf("unmounting mount %q: %v", intermediateMount, err)
185+
}
186+
if err := os.Remove(intermediateMount); err != nil {
187+
logrus.Debugf("removing should-be-empty mount directory %q: %v", intermediateMount, err)
188+
}
189+
}
190+
for _, mountedImage := range mountedImages {
191+
if _, err := store.UnmountImage(mountedImage, false); err != nil {
192+
logrus.Debugf("unmounting image %q: %v", mountedImage, err)
193+
}
194+
}
195+
// unlock if any locked files from this RUN statement
196+
internalParse.UnlockLockArray(lockedTargets)
197+
}()
166198
options.Mounts = mounts
167-
// Run() will automatically clean them up.
168-
options.ExternalImageMounts = mountedImages
169199
options.CgroupManager = globalFlagResults.CgroupManager
170200

171201
runerr := builder.Run(args, options)
@@ -181,31 +211,5 @@ func runCmd(c *cobra.Command, args []string, iopts runInputOptions) error {
181211
conditionallyAddHistory(builder, c, "%s %s", shell, strings.Join(args, " "))
182212
return builder.Save()
183213
}
184-
// unlock if any locked files from this RUN statement
185-
for _, path := range lockedTargets {
186-
_, err := os.Stat(path)
187-
if err != nil {
188-
// Lockfile not found this might be a problem,
189-
// since LockedTargets must contain list of all locked files
190-
// don't break here since we need to unlock other files but
191-
// log so user can take a look
192-
logrus.Warnf("Lockfile %q was expected here, stat failed with %v", path, err)
193-
continue
194-
}
195-
lockfile, err := lockfile.GetLockfile(path)
196-
if err != nil {
197-
// unable to get lockfile
198-
// lets log error and continue
199-
// unlocking other files
200-
logrus.Warn(err)
201-
continue
202-
}
203-
if lockfile.Locked() {
204-
lockfile.Unlock()
205-
} else {
206-
logrus.Warnf("Lockfile %q was expected to be locked, this is unexpected", path)
207-
continue
208-
}
209-
}
210214
return runerr
211215
}

define/types.go

+23-17
Original file line numberDiff line numberDiff line change
@@ -106,13 +106,13 @@ type BuildOutputOption struct {
106106
IsStdout bool
107107
}
108108

109-
// TempDirForURL checks if the passed-in string looks like a URL or -. If it is,
110-
// TempDirForURL creates a temporary directory, arranges for its contents to be
111-
// the contents of that URL, and returns the temporary directory's path, along
112-
// with the name of a subdirectory which should be used as the build context
113-
// (which may be empty or "."). Removal of the temporary directory is the
114-
// responsibility of the caller. If the string doesn't look like a URL,
115-
// TempDirForURL returns empty strings and a nil error code.
109+
// TempDirForURL checks if the passed-in string looks like a URL or "-". If it
110+
// is, TempDirForURL creates a temporary directory, arranges for its contents
111+
// to be the contents of that URL, and returns the temporary directory's path,
112+
// along with the relative name of a subdirectory which should be used as the
113+
// build context (which may be empty or "."). Removal of the temporary
114+
// directory is the responsibility of the caller. If the string doesn't look
115+
// like a URL or "-", TempDirForURL returns empty strings and a nil error code.
116116
func TempDirForURL(dir, prefix, url string) (name string, subdir string, err error) {
117117
if !strings.HasPrefix(url, "http://") &&
118118
!strings.HasPrefix(url, "https://") &&
@@ -125,19 +125,24 @@ func TempDirForURL(dir, prefix, url string) (name string, subdir string, err err
125125
if err != nil {
126126
return "", "", fmt.Errorf("error creating temporary directory for %q: %w", url, err)
127127
}
128+
downloadDir := filepath.Join(name, "download")
129+
if err = os.MkdirAll(downloadDir, 0o700); err != nil {
130+
return "", "", fmt.Errorf("creating directory %q for %q: %w", downloadDir, url, err)
131+
}
128132
urlParsed, err := urlpkg.Parse(url)
129133
if err != nil {
130134
return "", "", fmt.Errorf("error parsing url %q: %w", url, err)
131135
}
132136
if strings.HasPrefix(url, "git://") || strings.HasSuffix(urlParsed.Path, ".git") {
133-
combinedOutput, gitSubDir, err := cloneToDirectory(url, name)
137+
combinedOutput, gitSubDir, err := cloneToDirectory(url, downloadDir)
134138
if err != nil {
135139
if err2 := os.RemoveAll(name); err2 != nil {
136140
logrus.Debugf("error removing temporary directory %q: %v", name, err2)
137141
}
138142
return "", "", fmt.Errorf("cloning %q to %q:\n%s: %w", url, name, string(combinedOutput), err)
139143
}
140-
return name, gitSubDir, nil
144+
logrus.Debugf("Build context is at %q", downloadDir)
145+
return name, filepath.Join(filepath.Base(downloadDir), gitSubDir), nil
141146
}
142147
if strings.HasPrefix(url, "github.com/") {
143148
ghurl := url
@@ -146,28 +151,29 @@ func TempDirForURL(dir, prefix, url string) (name string, subdir string, err err
146151
subdir = path.Base(ghurl) + "-master"
147152
}
148153
if strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://") {
149-
err = downloadToDirectory(url, name)
154+
err = downloadToDirectory(url, downloadDir)
150155
if err != nil {
151156
if err2 := os.RemoveAll(name); err2 != nil {
152157
logrus.Debugf("error removing temporary directory %q: %v", name, err2)
153158
}
154-
return "", subdir, err
159+
return "", "", err
155160
}
156-
return name, subdir, nil
161+
logrus.Debugf("Build context is at %q", filepath.Join(downloadDir, subdir))
162+
return name, filepath.Join(filepath.Base(downloadDir), subdir), nil
157163
}
158164
if url == "-" {
159-
err = stdinToDirectory(name)
165+
err = stdinToDirectory(downloadDir)
160166
if err != nil {
161167
if err2 := os.RemoveAll(name); err2 != nil {
162168
logrus.Debugf("error removing temporary directory %q: %v", name, err2)
163169
}
164-
return "", subdir, err
170+
return "", "", err
165171
}
166-
logrus.Debugf("Build context is at %q", name)
167-
return name, subdir, nil
172+
logrus.Debugf("Build context is at %q", filepath.Join(downloadDir, subdir))
173+
return name, filepath.Join(filepath.Base(downloadDir), subdir), nil
168174
}
169175
logrus.Debugf("don't know how to retrieve %q", url)
170-
if err2 := os.Remove(name); err2 != nil {
176+
if err2 := os.RemoveAll(name); err2 != nil {
171177
logrus.Debugf("error removing temporary directory %q: %v", name, err2)
172178
}
173179
return "", "", errors.New("unreachable code reached")

docs/buildah-run.1.md

+8-6
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ BUILDAH\_ISOLATION environment variable. `export BUILDAH_ISOLATION=oci`
9999

100100
Attach a filesystem mount to the container
101101

102-
Current supported mount TYPES are bind, cache, secret and tmpfs. <sup>[[1]](#Footnote1)</sup>
102+
Current supported mount TYPES are bind, cache, secret and tmpfs. Writes to `bind` and `tmpfs` mounts are discarded after the command finishes, while changes to `cache` mounts persist across uses. <sup>[[1]](#Footnote1)</sup>
103103

104104
e.g.
105105

@@ -111,19 +111,19 @@ Current supported mount TYPES are bind, cache, secret and tmpfs. <sup>[[1]](#Foo
111111

112112
Common Options:
113113

114-
· src, source: mount source spec for bind and volume. Mandatory for bind. If `from` is specified, `src` is the subpath in the `from` field.
114+
· src, source: mount source spec for bind and cache. Mandatory for bind. If `from` is specified, `src` is the subpath in the `from` field.
115115

116-
· dst, destination, target: mount destination spec.
116+
· dst, destination, target: location where the command being run should see the content being mounted.
117117

118-
· ro, read-only: true or false (default).
118+
· ro, read-only: (default true for `type=bind`, false for `type=tmpfs`, `type=cache`).
119119

120120
Options specific to bind:
121121

122122
· bind-propagation: shared, slave, private, rshared, rslave, or rprivate(default). See also mount(2).
123123

124124
. bind-nonrecursive: do not setup a recursive bind mount. By default it is recursive.
125125

126-
· from: stage or image name for the root of the source. Defaults to the build context.
126+
· from: image name for the root of the source. Defaults to **--contextdir**, mandatory if **--contextdir** was not specified.
127127

128128
· z: Set shared SELinux label on mounted destination. Use if SELinux is enabled on host machine.
129129

@@ -143,7 +143,7 @@ Current supported mount TYPES are bind, cache, secret and tmpfs. <sup>[[1]](#Foo
143143

144144
Options specific to cache:
145145

146-
· id: Create a separate cache directory for a particular id.
146+
· id: Distinguish this cache from other caches using this ID rather than the target mount path.
147147

148148
· mode: File mode for new cache directory in octal. Default 0755.
149149

@@ -155,6 +155,8 @@ Current supported mount TYPES are bind, cache, secret and tmpfs. <sup>[[1]](#Foo
155155

156156
· from: stage name for the root of the source. Defaults to host cache directory.
157157

158+
· sharing: Whether other users of this cache need to wait for this command to complete (`sharing=locked`) or not (`sharing=shared`, which is the default).
159+
158160
· z: Set shared SELinux label on mounted destination. Use if SELinux is enabled on host machine.
159161

160162
· Z: Set private SELinux label on mounted destination. Use if SELinux is enabled on host machine.

imagebuildah/stage_executor.go

+17-4
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,11 @@ func (s *StageExecutor) runStageMountPoints(mountList []string) (map[string]inte
516516
// to `mountPoint` replaced from additional
517517
// build-context. Reason: Parser will use this
518518
// `from` to refer from stageMountPoints map later.
519-
stageMountPoints[from] = internal.StageMountDetails{IsStage: false, MountPoint: mountPoint}
519+
stageMountPoints[from] = internal.StageMountDetails{
520+
IsAdditionalBuildContext: true,
521+
IsImage: true,
522+
MountPoint: mountPoint,
523+
}
520524
break
521525
} else {
522526
// Most likely this points to path on filesystem
@@ -548,7 +552,10 @@ func (s *StageExecutor) runStageMountPoints(mountList []string) (map[string]inte
548552
mountPoint = additionalBuildContext.DownloadedCache
549553
}
550554
}
551-
stageMountPoints[from] = internal.StageMountDetails{IsStage: true, MountPoint: mountPoint}
555+
stageMountPoints[from] = internal.StageMountDetails{
556+
IsAdditionalBuildContext: true,
557+
MountPoint: mountPoint,
558+
}
552559
break
553560
}
554561
}
@@ -559,14 +566,20 @@ func (s *StageExecutor) runStageMountPoints(mountList []string) (map[string]inte
559566
return nil, err
560567
}
561568
if otherStage, ok := s.executor.stages[from]; ok && otherStage.index < s.index {
562-
stageMountPoints[from] = internal.StageMountDetails{IsStage: true, MountPoint: otherStage.mountPoint}
569+
stageMountPoints[from] = internal.StageMountDetails{
570+
IsStage: true,
571+
MountPoint: otherStage.mountPoint,
572+
}
563573
break
564574
} else {
565575
mountPoint, err := s.getImageRootfs(s.ctx, from)
566576
if err != nil {
567577
return nil, fmt.Errorf("%s from=%s: no stage or image found with that name", flag, from)
568578
}
569-
stageMountPoints[from] = internal.StageMountDetails{IsStage: false, MountPoint: mountPoint}
579+
stageMountPoints[from] = internal.StageMountDetails{
580+
IsImage: true,
581+
MountPoint: mountPoint,
582+
}
570583
break
571584
}
572585
default:

internal/open/open.go

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package open
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"syscall"
7+
)
8+
9+
// InChroot opens the file at `path` after chrooting to `root` and then
10+
// changing its working directory to `wd`. Both `wd` and `path` are evaluated
11+
// in the chroot.
12+
// Returns a file handle, an Errno value if there was an error and the
13+
// underlying error was a standard library error code, and a non-empty error if
14+
// one was detected.
15+
func InChroot(root, wd, path string, mode int, perm uint32) (fd int, errno syscall.Errno, err error) {
16+
requests := requests{
17+
Root: root,
18+
Wd: wd,
19+
Open: []request{
20+
{
21+
Path: path,
22+
Mode: mode,
23+
Perms: perm,
24+
},
25+
},
26+
}
27+
results := inChroot(requests)
28+
if len(results.Open) != 1 {
29+
return -1, 0, fmt.Errorf("got %d results back instead of 1", len(results.Open))
30+
}
31+
if results.Open[0].Err != "" {
32+
if results.Open[0].Errno != 0 {
33+
err = fmt.Errorf("%s: %w", results.Open[0].Err, results.Open[0].Errno)
34+
} else {
35+
err = errors.New(results.Open[0].Err)
36+
}
37+
}
38+
return int(results.Open[0].Fd), results.Open[0].Errno, err
39+
}

0 commit comments

Comments
 (0)