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

range_to_pow_2_blocks can underflow length (size_t) #567

Open
pentelbart opened this issue Oct 11, 2022 · 7 comments
Open

range_to_pow_2_blocks can underflow length (size_t) #567

pentelbart opened this issue Oct 11, 2022 · 7 comments

Comments

@pentelbart
Copy link

pentelbart commented Oct 11, 2022

From backend_helpers/range_helpers.h

  template<size_t MIN_BITS, SNMALLOC_CONCEPT(capptr::IsBound) B, typename F>
  void range_to_pow_2_blocks(CapPtr<void, B> base, size_t length, F f)
  {
    auto end = pointer_offset(base, length);
    base = pointer_align_up(base, bits::one_at_bit(MIN_BITS));
    end = pointer_align_down(end, bits::one_at_bit(MIN_BITS));
    length = pointer_diff(base, end);

    bool first = true;

    // Find the minimum set of maximally aligned blocks in this range.
    // Each block's alignment and size are equal.
    while (length >= sizeof(void*))
    {
      size_t base_align_bits = bits::ctz(address_cast(base));
      size_t length_align_bits = (bits::BITS - 1) - bits::clz(length);
      size_t align_bits = bits::min(base_align_bits, length_align_bits);
      size_t align = bits::one_at_bit(align_bits);

      /*
       * Now that we have found a maximally-aligned block, we can set bounds
       * and be certain that we won't hit representation imprecision.
       */
      f(base, align, first);
      first = false;

      base = pointer_offset(base, align);
      length -= align;
    }
  }

Length can underflow here. As a result, the loop calls f(base, align, first) on ever-increasing values for base. This leads to runtime length-violations on CHERI RISC-V.

I've taken a blunt instrument to this locally in the form of:

      ...
      base = pointer_offset(base, align);
      if (length < align)
        return;
      length -= align;
      ...

This eliminates the crashes I was seeing, but I'm not sure this is the proper solution.

@pentelbart
Copy link
Author

Example values for this are: 0x40b2d000 and 0 for base and length respectively.

@mjp41
Copy link
Member

mjp41 commented Oct 11, 2022

So would

    ...
    auto end = pointer_offset(base, length);
    base = pointer_align_up(base, bits::one_at_bit(MIN_BITS));
    end = pointer_align_down(end, bits::one_at_bit(MIN_BITS));
    length = pointer_diff(base, end);

    if (base >= end)
      return;

    bool first = true;
    ...

Fix the issue?

@pentelbart
Copy link
Author

So would

    ...
    auto end = pointer_offset(base, length);
    base = pointer_align_up(base, bits::one_at_bit(MIN_BITS));
    end = pointer_align_down(end, bits::one_at_bit(MIN_BITS));
    length = pointer_diff(base, end);

    if (base >= end)
      return;

    bool first = true;
    ...

Fix the issue?

A version of that adjusted for the fact that these are CapPtr appears to, however this issue sometimes is coy about presenting. Tentatively yes.

@pentelbart
Copy link
Author

Does missing the call to f potentially have consequences in those cases?

@nwf
Copy link
Contributor

nwf commented Oct 11, 2022

Should we be asserting that we call this function only with lengths >= bits::one_at_bit(MIN_BITS)? Where is that not true?

@mjp41
Copy link
Member

mjp41 commented Oct 12, 2022

Does missing the call to f potentially have consequences in those cases?

It shouldn't mater if we don't call f. It should be callable multiple times, and zero.

Should we be asserting that we call this function only with lengths >= bits::one_at_bit(MIN_BITS)? Where is that not true?

That sounds like a good assert to add.

@pentelbart how did your system end up calling this code with zero?

@pentelbart
Copy link
Author

pentelbart commented Oct 15, 2022

It was called from LargeBuddyRange::refill and the original allocation was 131,072 bytes, however the stack trace obscured some arguments further down the call stack so it's hard to be definite.

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

No branches or pull requests

3 participants