Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure umask is unset when extracting archive #727

Merged
merged 23 commits into from
Sep 30, 2021

Conversation

natalieparellano
Copy link
Member

Thanks @yaelharel for prodding me to look into this more closely

#203 added logic to update file permissions in case the system umask was applied.
#246 unset the umask before the extract to avoid having to do this extra step (thus saving time), however it made the function not thread-safe as discovered in #719.

This PR ensures that

  • the umask is always unset when running the extract
  • the system umask is applied to directories
  • extracts running in parallel will not contaminate each other

@sclevine it looks like you have some of the history here, it would be great if you could take a look!

cc @aemengo @jabrown85

@natalieparellano natalieparellano requested a review from a team as a code owner September 28, 2021 15:30
Signed-off-by: Natalie Arellano <[email protected]>
Signed-off-by: Natalie Arellano <[email protected]>
Signed-off-by: Natalie Arellano <[email protected]>
Signed-off-by: Natalie Arellano <[email protected]>
Signed-off-by: Natalie Arellano <[email protected]>
@jabrown85
Copy link
Contributor

jabrown85 commented Sep 29, 2021

Can we, instead of trying to make Extract thread-safe handle umask, put the umask reset logic in restorer?

This is the bit that causes the problem, if I'm understanding this correctly.

Could we make (r *Restorer) Restore(cache Cache) error have the init like we used to have?

func init() {
	systemUmask = setUmask(0)
	_ = setUmask(systemUmask)
}

func (r *Restorer) Restore(cache Cache) error {
	umask = setUmask(0)      
        defer setUmask(systemUmask)
        ... some time later ...
        go layers.Extract(rc, "", umask) // add umask as param to Extract
}

We don't expect Restorer to be used concurrently and we know Extract is used concurrently so it makes sense to me to move the unsafe bits up a level.

Or, if we really don't want to pass umask from restorer down to archive, we could have archive.Extract only execute umask(0) if /proc/self/status umask is not already zero and always return the previous umask if it had to set it so the caller can do something like this so Extract is safe for reentry.

var firstUmask

go func(){
   umask, err := layers.Extract(rc, "")
   if umask != -1 {
    firstUmask = umask
  }
}

// after all are done
setUmask(prevUmask)

@natalieparellano
Copy link
Member Author

put the umask reset logic in restorer

The latest incarnation of this PR does this. archive.Extract doesn't do anything except verify that the umask is already 0. Maybe we could take the verification out, and add a docs comment, but that would make the function buggy for anyone who didn't read carefully, which seems bad.

Could we make (r *Restorer) Restore(cache Cache) error have the init like we used to have?

What would the variable set in the init be used for?

we could have archive.Extract only execute umask(0) if /proc/self/status umask is not already zero and always return the previous umask if it had to set it

This is interesting. I had some variation of this before, but I was erroring out if the umask was not already 0 (and trying to "set it back" within archive.Extract, which is unsafe). Let me give it a try.

@natalieparellano
Copy link
Member Author

@jabrown85 one bit that I think makes what you proposed hard is that the original umask is also used within archive.Extract, here:

if err := os.MkdirAll(dirPath, applyUmask(os.ModePerm, umask)); err != nil {

Signed-off-by: Natalie Arellano <[email protected]>
@natalieparellano
Copy link
Member Author

After conversation with @jabrown85, we decided:

  • Getting the system umask in an init function is potentially too early (a program could alter it later on)
  • We don't need to "check the current umask by unsetting it" inside archive.Extract if we document that the umask must be unset prior to calling the function
  • We don't entirely trust our homegrown GetUmask()

The latest commits reflect this thinking. It's a bit gross to change the signature of archive.Extract and layers.Extract, but it's safer this way (we think).

Signed-off-by: Natalie Arellano <[email protected]>
Signed-off-by: Natalie Arellano <[email protected]>
Signed-off-by: Natalie Arellano <[email protected]>
Signed-off-by: Natalie Arellano <[email protected]>
Signed-off-by: Natalie Arellano <[email protected]>
Signed-off-by: Natalie Arellano <[email protected]>
Signed-off-by: Natalie Arellano <[email protected]>
Signed-off-by: Natalie Arellano <[email protected]>
Copy link
Contributor

@aemengo aemengo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very elegant!

Signed-off-by: Natalie Arellano <[email protected]>
@natalieparellano
Copy link
Member Author

The arm worker... 😭

@@ -167,3 +178,12 @@ func testExtract(t *testing.T, when spec.G, it spec.S) {
})
})
}

func testPathPerms(t *testing.T, parentDir, path string, expectedMode os.FileMode) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're calling this function only in one use case, maybe we can put it inside the for loop. I'm not sure what will be clearer for the user.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aye, I called this function in two places before and forgot to change it back. I would update but I'm having problems with my IDE... let's just leave it out of laziness.

Copy link
Contributor

@yaelharel yaelharel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a few small comments but I don't have strong opinion on making those changes.

Signed-off-by: Natalie Arellano <[email protected]>
@codecov
Copy link

codecov bot commented Sep 30, 2021

Codecov Report

Merging #727 (c8addc4) into release/0.12.0 (7e50a62) will increase coverage by 0.10%.
The diff coverage is 37.50%.

❗ Current head c8addc4 differs from pull request most recent head 9f3d758. Consider uploading reports for the commit 9f3d758 to get more accurate results
Impacted file tree graph

@@                Coverage Diff                 @@
##           release/0.12.0     #727      +/-   ##
==================================================
+ Coverage           64.65%   64.74%   +0.10%     
==================================================
  Files                  52       52              
  Lines                3668     3669       +1     
==================================================
+ Hits                 2371     2375       +4     
+ Misses               1048     1045       -3     
  Partials              249      249              
Flag Coverage Δ
os_windows 64.74% <37.50%> (+0.10%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

@natalieparellano natalieparellano merged commit 3051985 into release/0.12.0 Sep 30, 2021
@natalieparellano natalieparellano deleted the unset-umask branch September 30, 2021 16:29
natalieparellano added a commit that referenced this pull request Oct 26, 2021
* Run image should be locked to a digest in analyzed.toml (#720)

* Run image should be locked to a digest in analyzed.toml

Signed-off-by: Natalie Arellano <[email protected]>

* Use more flexible matcher for other test

Signed-off-by: Natalie Arellano <[email protected]>

* Update github actions to use cosign v1.2.0 (#708)

* Introduce new api version helpers (#705)

* Introduce new api version helpers

This makes the code a little easier to read.

Signed-off-by: Natalie Arellano <[email protected]>

* Fix

Signed-off-by: Natalie Arellano <[email protected]>

* Remove comment

Signed-off-by: Natalie Arellano <[email protected]>

* Fix lint

Signed-off-by: Natalie Arellano <[email protected]>

* Update github actions to use cosign v1.2.0

Signed-off-by: Sambhav Kothari <[email protected]>

Co-authored-by: Natalie Arellano <[email protected]>

* Add information about buildpacksio/lifecycle (#707)

* Introduce new api version helpers (#705)

* Introduce new api version helpers

This makes the code a little easier to read.

Signed-off-by: Natalie Arellano <[email protected]>

* Fix

Signed-off-by: Natalie Arellano <[email protected]>

* Remove comment

Signed-off-by: Natalie Arellano <[email protected]>

* Fix lint

Signed-off-by: Natalie Arellano <[email protected]>

* Add information about buildpacksio/lifecycle

This information should be copied to the Docker Hub repo "about" section.

Signed-off-by: Natalie Arellano <[email protected]>

* Small fix

Signed-off-by: Natalie Arellano <[email protected]>

* Small fix

Signed-off-by: Natalie Arellano <[email protected]>

* Update steps for verifying SBOM

Signed-off-by: Natalie Arellano <[email protected]>

* Update the README for platform 0.7 (#704)

Signed-off-by: Natalie Arellano <[email protected]>

* Fix umask race (#722)

* Set umask before extracting layers to avoid race condition

Signed-off-by: Natalie Arellano <[email protected]>

* Add comment

Signed-off-by: Natalie Arellano <[email protected]>

* Update archive/extract.go

Signed-off-by: Natalie Arellano <[email protected]>

Co-authored-by: Anthony Emengo <[email protected]>

* Don't try to set the umask outside of extract

Signed-off-by: Natalie Arellano <[email protected]>

* Don't try to read umask in extract

Signed-off-by: Natalie Arellano <[email protected]>

Co-authored-by: Anthony Emengo <[email protected]>

* Buildpack api 0.7 is not supported (#726)

* Buildpack api 0.7 is not supported

We missed this when backing out asset packages.

Signed-off-by: Natalie Arellano <[email protected]>

* Fix

Signed-off-by: Natalie Arellano <[email protected]>

* Use the correct tag when signing the sbom (#729)

* Use the correct tag when signing the sbom

Also there is no need to parse the digest from `crane tag` because it does not change.
This will make the code less brittle.

Signed-off-by: Natalie Arellano <[email protected]>

* Add manifest sha when validating semver

Signed-off-by: Natalie Arellano <[email protected]>

* Fix

Signed-off-by: Natalie Arellano <[email protected]>

* Fix

Signed-off-by: Natalie Arellano <[email protected]>

* Ensure umask is unset when extracting archive (#727)

* Ensure umask is unset when extracting archive

Signed-off-by: Natalie Arellano <[email protected]>

* Add test

Signed-off-by: Natalie Arellano <[email protected]>

* Fix

Signed-off-by: Natalie Arellano <[email protected]>

* Get the current umask without changing it

Signed-off-by: Natalie Arellano <[email protected]>

* Fix

Signed-off-by: Natalie Arellano <[email protected]>

* Fix windows

Signed-off-by: Natalie Arellano <[email protected]>

* Fix windows

Signed-off-by: Natalie Arellano <[email protected]>

* Update per review comments

Signed-off-by: Natalie Arellano <[email protected]>

* Less confusing wording

Signed-off-by: Natalie Arellano <[email protected]>

* Reduce the diff

Signed-off-by: Natalie Arellano <[email protected]>

* Fix

Signed-off-by: Natalie Arellano <[email protected]>

* Added comments

Signed-off-by: Natalie Arellano <[email protected]>

* Better wording

Signed-off-by: Natalie Arellano <[email protected]>

* Add test that system umask is used to create non existent directory not in tar file

Signed-off-by: Natalie Arellano <[email protected]>

* Variable names and formatting

Signed-off-by: Natalie Arellano <[email protected]>

* Try to fix windows

Signed-off-by: Natalie Arellano <[email protected]>

* Avoid direct dependency on archive

Signed-off-by: Natalie Arellano <[email protected]>

* Make test setup simpler and update comment

Signed-off-by: Natalie Arellano <[email protected]>

* Add build directive

Signed-off-by: Natalie Arellano <[email protected]>

* Apply suggestions from code review

Signed-off-by: Natalie Arellano <[email protected]>

* Fix Codecov

Signed-off-by: Natalie Arellano <[email protected]>

* Fix lint

Signed-off-by: Natalie Arellano <[email protected]>

* Set Umask as part of archive.Extract

Signed-off-by: Natalie Arellano <[email protected]>

* Move the unlock methods to be under defer
instead at the end of the function in case setUmask will panic.

Signed-off-by: Natalie Arellano <[email protected]>

* Bump imgutil (#731)

Signed-off-by: Natalie Arellano <[email protected]>

* Fix merge

Signed-off-by: Natalie Arellano <[email protected]>

Co-authored-by: Sambhav Kothari <[email protected]>
Co-authored-by: Anthony Emengo <[email protected]>
Co-authored-by: Yael Harel <[email protected]>
Co-authored-by: Yael Harel <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants