-
Notifications
You must be signed in to change notification settings - Fork 47
Fix daemon SEGV when invalid chain type is passed #353
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
Conversation
07c8d7a to
d18a3d8
Compare
qdeslandes
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
About the commits titles:
libbpfilter: Add some out-of-bounds checks->lib: add some out-of-bounds checksbfcli: Fix parsing unknown hook types->cli: fix parsing unknown hook typesdaemon: Reject invalid hook types->daemon: reject invalid hook types
Most of those comments are due to stuff that is not properly formalized and described in the doc, mostly because is evolved over time (hence, not applied to the whole codebase).
| int hook = bf_hook_from_str($1); | ||
| if (hook < 0) | ||
| bf_parse_err("unknown hook '%s'\n", $1); | ||
|
|
||
| free($1); | ||
| $$ = hook; | ||
| $$ = (enum bf_hook)hook; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not necessary. bf_hook_from_str() returns a enum bf_hook type, we should store it as such. return -EINVAL is a special error case which can be dealt with in using (hook < 0) even if hook is enum bf_hook type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sadly, seems necessary. Storing negative values in enum is UB, and GCC optimizes that if away, even in -O0.
Godbolt: https://godbolt.org/z/MvWzob36e
Meta internal explanation: M8N33B
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh no :(
Alright, let's find a different approach then. Another approach could be:
int bf_hook_from_str(const char *str, enum bf_hook *ret). This way we have a return value to check, and we can use ret on success. It's a bit more verbose, but at least our back's covered.
The other option would be to use int bf_hook_from_str(const char *str) and cast the return to enum bf_hook after it's checked. I find the suggestion above better as there's clear distinction between the return value and the parsed value. What do you think?
Oooooor, we can return the sentinel value on error: _BF_HOOK_MAX if the string can't be parsed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
int bf_hook_from_str(const char *str, enum bf_hook *ret)
I like this approach the most. Now I'm thinking about redoing these commits from scratch, and adding validation only to the two points where we accept user input:
- for CLI, change
bf_hook_from_strlike you described - for daemon, change
bf_rpack_kv_enum(node, key, value)tobf_rpack_kv_enum(node, key, value, min, max)and validate the input value when parsing the request instead of later
Seems other parsing, for example bf_matcher_type_from_str, already uses that approach.
d18a3d8 to
37ae208
Compare
| int hook = bf_hook_from_str($1); | ||
| if (hook < 0) | ||
| bf_parse_err("unknown hook '%s'\n", $1); | ||
|
|
||
| free($1); | ||
| $$ = hook; | ||
| $$ = (enum bf_hook)hook; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh no :(
Alright, let's find a different approach then. Another approach could be:
int bf_hook_from_str(const char *str, enum bf_hook *ret). This way we have a return value to check, and we can use ret on success. It's a bit more verbose, but at least our back's covered.
The other option would be to use int bf_hook_from_str(const char *str) and cast the return to enum bf_hook after it's checked. I find the suggestion above better as there's clear distinction between the return value and the parsed value. What do you think?
Oooooor, we can return the sentinel value on error: _BF_HOOK_MAX if the string can't be parsed.
|
Closing in favor of #366 stack |
I was playing with bpfilter, and typo-ed a bfcli command. To my surprise, it caused a SIGSEGV crash in daemon process. That's because bfcli parser returned
-22(EINVAL), which was passed as-is to the daemon, which used it to index into an array, leading to out-of-bounds array access.I'm introducing 3 changes here:
libbpfilter: Add some out-of-bounds checks- adding somebf_assertchecks in helper functions that read from arrays, so that in debug builds we get a cleaner signalbfcli: Fix parsing unknown hook types- instead of passing EINVAL along, print an error to the user and abortdaemon: Reject invalid hook types- some hardening on server side, to turn this crash into an error with easy-to-understand message