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

fix(driver/bpf): bpf_addr_to_kernel takes an unsigned length (less than BPF_MAX_VAR_SIZE) #1730

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from

Conversation

leodido
Copy link
Contributor

@leodido leodido commented Jan 14, 2021

The bpf_syscall_get_argument function returns an unsigned long that
was assigned to an int and passed to the bpf_addr_to_kernel function (ulen
variable).

We need to ensure the value is always unsigned (thus removing the inner
check > 0).

We also need to ensure it is not bigger than 1ULL << 29 (ie.,
BPF_MAX_VAR_SIZE).

Otherwise, the verifier gives un an "unbounded memory access" error.
This happens for Linux kernels (eg., >= 5.8) containing this
patch
.

Refs #1658

Co-authored-by: Lorenzo Fontana lo@linux.com
Signed-off-by: Leonardo Di Donato leodidonato@gmail.com

BPF_MAX_VAR_SIZE)

The `bpf_syscall_get_argument` function returns an `unsigned long` that
was assigned to an `int` and passed to `bpf_addr_to_kernel` (`ulen`
variable).

We need to enusre the value is always unsigned (thus removing the inner
check for it being greater than 0).

We also need to ensure it is not bigger than `1ULL << 29` (ie.,
`BPF_MAX_VAR_SIZE`).

Otherwise the verifier gives un an "unbounded memory access" error.
This happens for Linux kernels containing [this
patch](https://lore.kernel.org/bpf/20190614072557.196239-10-ast@kernel.org/).

Co-authored-by: Lorenzo Fontana <lo@linux.com>
Signed-off-by: Leonardo Di Donato <leodidonato@gmail.com>
driver/bpf/filler_helpers.h Outdated Show resolved Hide resolved
struct sockaddr *kaddr)
{
if (ulen < 0 || ulen > sizeof(struct sockaddr_storage))
if (ulen > BPF_MAX_VAR_SIZE)
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems to increase the valid range of ulen. Previously the function would return -EINVAL if ulen > sizeof(struct sockaddr_storage), but on my machine that sizeof operation resolves to 128, which is far, far less than BPF_MAX_VAR_SIZE. Is this intended?

Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like we're doing ulen & 0xff (more or less) below. I feel that we should return an error whenever ulen > 0xff (otherwise the caller can request e.g. 0x1ff bytes--the value comes straight from userspace--while we copy 0xff and return success).

The original check against sizeof(struct sockaddr_storage) feels more correct.

Also, not sure what exactly BPF_FORBIDS_ZERO_ACCESS implies but if my guess (length must be > 0) is correct, both the #ifdef and the & 0xff (if we revert the size check) is unnecessary (ulen will always be > 0 after line 224 and < 0xff after original line 218).

Though I expect both are there to keep the verifier happy?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That check seems like a leftover from move_addr_to_kernel kernel function.

Anyways, we read at maximum ulen & 0xff every time (see bpf_probe_read below).

After some intensive and day-long testing, we (@fntlnz and I) have not been able to break it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@gnosek The verifier rejects a check against sizeof(struct sockaddr_storage).

Copy link
Contributor Author

@leodido leodido Jan 15, 2021

Choose a reason for hiding this comment

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

Anyway the bug here was about the int (now unsigned long) parameter and the missing boundary check.

Both were required by the verifier (giving two different errors on each of them). Thus, this is the scope of this PR.

In case we'd like to optimize the behaviour of the mask and the presence (or not) of ifdefs, we should approach those in another PR, imho.

As per our testing, they do not interfere with the correct functioning of the probe.

Copy link
Contributor

Choose a reason for hiding this comment

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

So if you replace BPF_MAX_VAR_SIZE with sizeof(struct sockaddr_storage), the verifier fails?

That is bizarre. That seems like a bug in the verifier.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, we tried in many many ways to retain that check too, but the verifier rejects it.

Signed-off-by: Leonardo Di Donato <leodidonato@gmail.com>
@leodido
Copy link
Contributor Author

leodido commented Jan 15, 2021

Hold until we test this on older verifiers (ie., older kernels).

fntlnz added a commit that referenced this pull request Feb 3, 2021
relocations for reads.

This commit also contains the verifier fixes from #1733 and #1730
to get to a loadable state, will likely need to remove them before merging.

Signed-off-by: Lorenzo Fontana <lo@linux.com>
@fntlnz fntlnz mentioned this pull request Feb 4, 2021
11 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants