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

Should verify that the max value is not too big #35

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

rhatdan
Copy link

@rhatdan rhatdan commented Apr 27, 2020

When parsing the ulimit, we should check if the ulimit max value is greater then
the processes max value. If yes then we should return an error.

Signed-off-by: Daniel J Walsh [email protected]

@rhatdan
Copy link
Author

rhatdan commented Apr 27, 2020

@vrothberg FYI

@vrothberg
Copy link

@AkihiroSuda @vdemeester @thaJeztah PTAL

ulimit.go Outdated
var rlimit syscall.Rlimit
if err := syscall.Getrlimit(limitType, &rlimit); err == nil {
if *hard > int64(rlimit.Max) {
return nil, fmt.Errorf("ulimit hard limit must be less than or equal to hard limit for the current process, hard: %d", *hard)

Choose a reason for hiding this comment

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

For debugging purposes, it could be helpful to print rlimit.Max as well.

return nil, fmt.Errorf("ulimit hard limit (%d) must be less than or equal to hard limit for the current process (%d)", *hard, rlimit.Max)

Copy link
Collaborator

@vdemeester vdemeester left a comment

Choose a reason for hiding this comment

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

LGTM 🐯

@thaJeztah
Copy link
Member

There's been some discussion around validation in the "go" code versus delegating to the kernel itself; does Linux return an error in this case, or is it silently ignored?

Copy link
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

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

Actually, I think this will also cause problems in situations where this function is used on a different host than where the process itself lives (e.g. the docker CLI uses this function to parse values, but that would now make the wrong assumption)

For your use-case, would it be possible to perform this validation after parsing the value?

@rhatdan
Copy link
Author

rhatdan commented Apr 28, 2020

Sure, the reason I wanted to do this hear was to avoid duplicating the data. Perhaps if we add a different function.

@rhatdan
Copy link
Author

rhatdan commented Aug 4, 2020

@thaJeztah Revamped this work, (Going through some old PR's) Now just added a verify function that will check against kernel. The problem is these values are just handed to different OCIs, and they can give you some useless information back to the user.

This should make it easier to diagnose the error, with a better error message.

Copy link
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

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

thanks for updating! I noticed that the current version would still cause some issues (also still a bit on the fence if the check should be in this code, or only the parsing, but I'm ok with adding)

ulimit.go Outdated
@@ -108,6 +109,17 @@ func ParseUlimit(val string) (*Ulimit, error) {
return &Ulimit{Name: parts[0], Soft: soft, Hard: *hard}, nil
}

// Verify that ulimit values work with current kernel
func (u *Ulimit) Verify() error {
var rlimit syscall.Rlimit
Copy link
Member

Choose a reason for hiding this comment

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

This won't compile on Windows (which would break the docker cli). Assuming we only need this on Linux/Unix-y systems (I guess just linux would do for now), could you move this to a _linux.go file (with the right build-tags), and create a stub implementation for other platforms? It could take the same approach as we did for the go-selinux package, and keep the exported function here, and a platform-specific, non-exported function in other files.

ulimit_test.go Outdated
@@ -21,6 +21,16 @@ func TestParseUlimitValid(t *testing.T) {
}
}

func TestParseUlimitTooBig(t *testing.T) {
Copy link
Member

Choose a reason for hiding this comment

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

This one should then also be in a ulimit_linux_test.go file

ulimit.go Outdated
@@ -4,6 +4,7 @@ import (
"fmt"
"strconv"
"strings"
"syscall"
Copy link
Member

Choose a reason for hiding this comment

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

Should probably be golang.org/x/sys, given that "syscall" is frozen/deprecated in favour of that package

@rhatdan rhatdan force-pushed the master branch 4 times, most recently from 579aaaf to 850fcf1 Compare August 4, 2020 20:05
@rhatdan
Copy link
Author

rhatdan commented Aug 4, 2020

@thaJeztah Decided to not wait 4 months again to update...

Copy link
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

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

thanks for updating! code changes look good to me, but unfortunately looks like the test may need some changes 😓 (see my comment inline)

)

func TestParseUlimitTooBig(t *testing.T) {
u, err := ParseUlimit("nofile=512:1000024")
Copy link
Member

Choose a reason for hiding this comment

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

I noticed CI isn't running on this repo (have to check why), but testing this locally in a container, the test failed.

--- FAIL: TestParseUlimitTooBig (0.00s)
    ulimit_linux_test.go:15: expected invalid hard limit

I added a debug line to Verify() (see below), which explains why it failed, because on this machine the limit is higher than what the test expects;

ulimit hard limit (1000024) is less than 1048576

Perhaps this test needs to dynamically set the value to make sure it's higher than the max. That's a bit ugly, so other suggestions welcome 😅)

diff --git a/ulimit_linux.go b/ulimit_linux.go
index a56f361..36a4b28 100644
--- a/ulimit_linux.go
+++ b/ulimit_linux.go
@@ -13,6 +13,7 @@ func (u *Ulimit) Verify() error {
 		if u.Hard > int64(rlimit.Max) {
 			return fmt.Errorf("ulimit hard limit (%d) must be less than or equal to hard limit for the current process %d",
 u.Hard, rlimit.Max)
 		}
+		fmt.Printf("ulimit hard limit (%d) is less than %d\n", u.Hard, rlimit.Max)
 	}
 	return nil
 }

Copy link
Contributor

Choose a reason for hiding this comment

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

@thaJeztah seems this issue was addressed -- the test case now gets the max value from the kernel.

When parsing the ulimit, we should check if the ulimit max value is greater then
the processes max value.  If yes then we should return an error.

Signed-off-by: Daniel J Walsh <[email protected]>
@thaJeztah
Copy link
Member

@thaJeztah seems this issue was addressed -- the test case now gets the max value from the kernel.

Ah, forgot about this one.

I'm still a bit on the fence if querying what the kernel supports, and validating against that is within scope of this module and/or if it should be more of a concern of the code using this module (so far the module was used to only parse values, and to validate a correct format, but no other assumptions) 🤔.

What are your thoughts on that, @kolyshkin ?

@rhatdan
Copy link
Author

rhatdan commented May 17, 2022

Two year old PR, that I totally forgot about. :^(

@kolyshkin
Copy link
Contributor

I'm on the verge about it. True, this package is only about parsing, it does not talk to the kernel in any way etc.

OTOH this package knows about ulimit, which is a UNIX concept, and adding Verify method is super easy here, and is not that easy in the other package (since the implementation uses a map which is internal to this package).

Overall I think this should go in, with the only change that Verify should not be specific to Linux -- looks like any unix (including *bsd and darwin) should support it. Perhaps even !windows (need to take a look at whatever x/sys/unix implements).

@kolyshkin
Copy link
Contributor

Another thing is, this raises the bar to how this should be tested -- at least we now need to compile this for various GOOS/GOARCH combinations.

@thaJeztah
Copy link
Member

Yes it's a bit of a tough one; I can definitely see the convenience of having the Validate(). Mostly concerned if it would be widening the scope too much (in addition to the dependency graph).

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.

6 participants